C++数据结构:并查集

目录

一. 并查集的概念

二. 并查集的模拟实现

2.1 并查集类的声明

2.2 并查集的实现

三. 路径压缩

四. 总结


一. 并查集的概念

在生活中,我们经常需要对某一些事物进行归类处理,即:将N个不同的元素划分为几个互不相交的集合。在初始状态,每个元素都属于一个独立的集合,在归一合并的过程中,经常需要查找某个元素属于哪一个集合,实现上面功能的数据结构,称之为并查集。

举一个生活中的例子,假设小卖部了有如下货物:苹果、香蕉、橘子、面包、薯片、牙膏、毛巾等,一开始进货的时候,这些货物是散装进货的,没有归类。一般而言,我们会将苹果、香蕉、橘子归类为水果,将面包和薯片归为零食,将牙膏和毛巾归为洗漱用品,这就相当于并查集查找元素所属的几何并进行归类的过程。初步归类后,继续将水果和零食统一归类为食物,这相当于合并两个类,并查集可以实现对类的合并。

并查集本质上是通过多颗树(森林)来实现的,每一颗树都表示一个独立的集合,使用双亲法表示树结构,即每个节点仅保存其父亲节点。如图1.1所示,将1~9十个数字分为三个集合,分别为{0,5,6,9}、{1,4,7}、{2,3,8},用三棵树表示对应关系,取{...}中第一个数据为根节点,使用vector<int>数组来实现双亲法表示树状结构,假设数据与vector下标直接对应,那么并查集划分集合的过程为:

  • 先将vector<int>中每个元素都设为-1,表示每个元素属于独立的集合。
  • 开始进行归一化,对于每个叶子节点,查找其根节点,根节点值-1,叶子节点位置处值为其父亲节点的下标。
  • 归类完成后,根节点下标处的值为负数,叶子节点下标处的值>=0
  • 对于叶子节点下标处的负数值,设其为rootVal,有:abs(rootVal) = 本集合的数据个数。
图1.1 并查集归类

如图1.2所示,将图1.1中的{0,5,6,9}和{1,4,7}归为一类,那么1就变为了0的孩子节点,合并后的几集合以0为根节点,有7个元素,因此vector<int>下标为0的位置处元素变为了-1,而原来下标为1的位置处的值变为合并后其父亲节点的值的下标,即0。

图1.2 并查集合并集合

二. 并查集的模拟实现

2.1 并查集类的声明

我们假设并查集要管理的元素值从1~N,不考虑模板类型数据和vector<int>下标的映射情况,根据第一节的内容,一个并查集类应当以下成员变量和接口:

  • std::vector<int> _ufs :用于以双亲表示法记录每一颗树(每一个集合)。
  • 构造函数 :给定数据量,将_ufs中的元素全部初始化为-1。
  • int FindRoot(int x),查找元素x所属的根在_ufs中的下标。
  • void Union(int x1, int x2) :将x1和x2所在的集合合并为一个集合。
  • size_t Size() :统计并查集内集合的个数。
  • 析构函数 :采用编译器默认生成的析构函数即可。

代码2.1:并查集类UnionFindSet的声明(UnionFindSet.hpp头文件)

#pragma once
#include <iostream>
#include <vector>
#include <algorithm>

// 并查集
class UnionFindSet
{
public:
	UnionFindSet(size_t size);
	int FindRoot(int x);         // 查找某个元素属于哪个集合(根下标)
	void Union(int x1, int x2);  // 合并两个集合
	size_t Size() const;         // 统计并查集内集合的个数

private:
	std::vector<int> _ufs;
};

2.2 并查集的实现

  • 构造函数:给定数据量size,将_ufs初始化为函数size个值为-1的线性表,表示每个元素都属于一个独立的集合。
  • 根查找函数int FindRoot(int x):本质上为查找元素属于哪一个集合,前面提到根下标处的元素为负数,因此只需要不断更新x = _ufs[x],直到_ufs[x] < x,然后将x返回即可。
  • 集合合并函数void Union(int x1, int x2):先查找两个元素的根root1和root2,如果root1 == root2成立,则表明两个元素属于同一集合,这种情况下直接返回即可。如果root1 != root2,那么就让_ufs[root1] += _ufs[root2],这样根节点下标位置处的绝对值就是集合中元素的个数,然后再执行_ufs[root2] = _ufs[root1],表示跟新被合并集合的根节点。
  • 统计集合个数函数size_t Size():统计根节点个数,即_ufs中小于0的元素的个数即可。

代码2.2:并查集的实现(UnionFindSet.cpp源文件)

#define _CRT_SECURE_NO_WARNINGS 1

#include "UnionFindSet.hpp"

UnionFindSet::UnionFindSet(size_t size)
	: _ufs(size, -1)
{ }

// 查找某个元素属于哪个集合(根下标)
int UnionFindSet::FindRoot(int x)
{
	// 循环查找,直到_ufs[x] < 0
	while (_ufs[x] >= 0)
	{
		x = _ufs[x];
	}
		
	return x;
}

// 合并两个集合
void UnionFindSet::Union(int x1, int x2)
{
	int root1 = FindRoot(x1);
	int root2 = FindRoot(x2);

	if (root1 == root2)
	{
		return;
	}

	if (root1 > root2)
	{
		std::swap(root1, root2);
	}

	_ufs[root1] += _ufs[root2];
	_ufs[root2] = root1;
}

// 统计并查集内集合的个数
size_t UnionFindSet::Size() const
{
	// 遍历_ufs统计小于0的元素的个数
	size_t count = 0;
	for (const auto x : _ufs)
	{
		if (x < 0) ++count;
	}
	return count;
}

三. 路径压缩

如果并查集中毒元素数量过多,那么每次查找根节点都需要花费较大的成本,那么,就需要采取适当的手段来进行路径压缩。

路径压缩最常用的方法:每当查找完一个元素所属集合的根节点后,都将这个元素的父亲节点直接设备根节点,这样当第二次查找这个元素的根节点时,就只需要向上查找一层即可。

如图3.1所示,假设要查找5的根节点,并进行路径压缩,只需要在找到5的根节点0后,将5挂在0的下面即可。当然,如果元素的数量不是很大,就没必要考虑路径压缩,因为路径压缩本身也存在资源消耗

图3.1 路径压缩

四. 总结

  • 并查集,是通过双签法实现多颗树(森林),来进行数据分类的数据结构。
  • 并查集能够实现的功能包括:查找元素所属集合(根节点)、合并集合、统计集合数目。
  • 如果并查集中元素的数目过多,那么应当进行路径压缩。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/169410.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何解决网站被攻击的问题:企业网络攻防的关键路径

在当今数字化时代&#xff0c;企业面临着不断升级的网络威胁&#xff0c;网站遭受攻击的风险也与日俱增。解决网站被攻击的问题对企业发展至关重要&#xff0c;不仅关系到企业的信息安全&#xff0c;也直接影响到企业的声誉和利益。从企业发展的角度出发&#xff0c;我们将探讨…

安装oracle19c卡在安装界面

我在个人window10电脑上安装 Oracle 19c 时遇到问题。解压后的数据库文件放在没有中文的文件目录下面&#xff0c;用管理员用户启动 CMD 窗口进行安装&#xff0c;但随后卡在菜单上。 取消安装之后去任务管理器中的服务里停掉OracleRemExecServiceV2服务。 用管理员运行CMD…

在VSCode创建vue项目,出现“因为在此系统上禁止运行脚本”问题

问题&#xff1a;vue : 无法加载文件 C:\Users\***\***\Roaming\npm\vue.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 ht tps:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 所在位置 行:1 字符: 1 解决&#xff…

【23真题】超难985!做完感觉没学过!

本套试卷难度分析&#xff1a;22年西北工业大学827考研真题&#xff0c;我也发布过&#xff0c;若有需要&#xff0c;戳这里自取&#xff01;本套试题内容有难度&#xff0c;题目考察全为大题&#xff0c;题目不多&#xff01;但是题目都很新颖&#xff0c;状态方程的题目考察较…

企业实现员工聊天和转账行为的实时监管

如何解决企业营销团队的管理问题&#xff1f; 在当今竞争激烈的市场环境中&#xff0c;企业营销团队的管理显得尤为重要。营销团队是企业发展的重要支柱&#xff0c;然而&#xff0c;一些常见的问题如员工飞单、私单、辱骂删除客户、离职带走公司客户以及工作不认真、工作量无…

吐槽一个 R package :DSS

TMD&#xff01;&#xff01;&#xff01; 前言 最近在整理WGBS分析的流程&#xff0c;下游需要找 Differentially Methylated Loci (DML) / Region (DMR)&#xff0c;类似普通转录组中的差异分析。之前看的一篇文章提到一个R package &#xff1a; DSS&#xff0c;看Biocond…

虹科示波器 | 汽车免拆检修 | 1994款凯迪拉克fleetwood车发动机无法起动

一、故障现象 一辆1994款凯迪拉克fleetwood车&#xff0c;搭载5.7L发动机&#xff08;燃油系统采用进气歧管多点喷射&#xff0c;每个气缸都有独立的喷油器&#xff1b;点火系统只有一个点火线圈&#xff0c;带机械分电器和高压线&#xff09;&#xff0c;发动机无法起动。 二、…

【PCB学习】几种接地符号

声明 该图并非原创&#xff0c;原文出处不可考&#xff0c;因此在这里附加说明。 示意图

使用ssh在本地环境(Windows)连接虚拟机以及其中的docker容器

配置虚拟机防火墙 防火墙的一系列操作需要root权限&#xff0c;默认是没有root密码的&#xff0c;所以首先需要设置root密码&#xff1a; sudo passwd root按提示完成root密码设置 切换到root账户 su root启用22端口并重启防火墙 firewall-cmd --permanent --add-port22/tc…

Redis篇---第十篇

系列文章目录 文章目录 系列文章目录前言一、怎么提高缓存命中率&#xff1f;二、Redis 如何解决 key 冲突&#xff1f;三、Redis 报内存不足怎么处理&#xff1f; 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分…

基于饥饿游戏算法优化概率神经网络PNN的分类预测 - 附代码

基于饥饿游戏算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于饥饿游戏算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于饥饿游戏优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

FPGA系列:1、FPGA/verilog源代码保护:基于Quartus13.1平台保护verilog源码发给第三方但不泄露源码

catlog 需求具体步骤工程描述去掉相关调试文件切换顶层模块并导出相应模块为网表文件切换回原顶层模块并添加相应保护模块的qxp文件再次编译工程 参考&#xff1a; 需求 有时需要将源码交付给第三方&#xff0c;但是源码中部分模块涉及到的核心代码无法暴漏给第三方。因此&…

IIC总线逻辑

一、 我们习以为常的IIC通常是什么样子&#xff1f; 在我们研发/应用工程师眼中&#xff0c;IIC的形象通常是如图这样的吧&#xff1f;&#xff08;你们说是不是&#xff1f;&#xff09; 是的&#xff0c;对于理想的硬件调程序&#xff0c;这个层…

改进YOLOv8:结合ConvNeXt V2骨干网络!使用MAE共同设计和扩展ConvNet

🗝️YOLOv8实战宝典--星级指南:从入门到精通,您不可错过的技巧   -- 聚焦于YOLO的 最新版本, 对颈部网络改进、添加局部注意力、增加检测头部,实测涨点 💡 深入浅出YOLOv8:我的专业笔记与技术总结   -- YOLOv8轻松上手, 适用技术小白,文章代码齐全,仅需 …

开发仿抖音APP遇到的问题和解决方案

uni-app如何引入阿里矢量库图标/uniapp 中引入 iconfont 文件报错文件查找失败 uni-app如何引入阿里矢量库图标 - 知乎 uniapp 中引入 iconfont 文件报错文件查找失败&#xff1a;‘./iconfont.woff?t1673007495384‘ at App.vue:6_宝马金鞍901的博客-CSDN博客 将课件中的cs…

8.6 矢量图层点要素基于规则(Rule-based)渲染使用

文章目录 前言基于规则&#xff08;Rule-based&#xff09;QGis代码实现 总结 前言 前面介绍了矢量-点要素-单一符号、矢量-点要素-分类符号以及矢量-点要素-分级符号的使用本章介绍如何使用基于规则的渲染说明&#xff1a;文章中的示例代码均来自开源项目qgis_cpp_api_apps …

Codeforces Round 908 (Div. 2)

一个教训&#xff1a;做题的时候一定要自己模拟一遍所有样例&#xff0c;这样思路出来的很快&#xff01;&#xff01;&#xff01; C. Anonymous Informant Example input Copy 6 5 3 4 3 3 2 3 3 100 7 2 1 5 5 6 1 1 1 1 1 1000000000 1 8 48 9 10 11 12 13 14 …

CTFHub Git泄露

Log 前言 根据题目描述&#xff0c;这个题目需要使用到工具 GitHack 来完成&#xff0c;而 CTFHub 上提供的工具需要在 python2 环境中执行&#xff0c;注意 python3 环境无法使用。 GitHack准备&#xff08;kali Linux&#xff09; 打开虚拟机 sudo su 以管理员的身份运行…

力扣刷题-二叉树-完全二叉树的节点个数

222.完全二叉树的节点个数 给出一个完全二叉树&#xff0c;求出该树的节点个数。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5,6] 输出&#xff1a;6 示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;0 示例 3&#xff1a; 输入&#xff1a;root [1]…

如何修改dpi为300?96dpi怎么改成300dpi?

平时使用的图片dpi一般都是96&#xff0c;但是我们在打印的时候&#xff0c;都要求dpi为300以上&#xff0c;这时候就需要修改图片分辨率&#xff0c;如何改图片分辨率成了一个问题&#xff0c;所以今天就教大家一个图片分辨率提高在线处理的方法&#xff0c;一起来了解一下吧。…