【高阶数据结构】并查集 -- 详解

embedded/2024/9/25 11:14:39/

一、并查集的原理

1、并查集的本质和概念

(1)本质

并查集的本质:森林。


(2)概念

在一些应用问题中,需要将 n 个不同的元素划分成一些不相交的集合

开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复用到查询某一个元素归属于那个集合的运算。适合于描述这类问题的抽象数据类型称为并查集(union-find set)


比如:某公司今年校招,在全国总共招生 10 人,广州招 4 人,深圳招 3 人,东莞招 3 人,10 个人来自不同的学校,起先互不相识,每个学生都是一个独立的小团体。

现给这些学生进行编号:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 给以下数组用来存储该小集体,数组中的数字代表:该小集体中具有成员的个数。

(初始状态为 -1 ,表示 10 棵树,即每个学生是一个独立的集合)

(跟堆类似,用下标表示关系,双亲表示法)

毕业后,学生们要去公司上班,每个地方的学生自发组织成小分队一起上路,于是:广州学生小分队 s1={0,6,7,8},深圳学生小分队 s2={1,4,9},东莞学生小分队 s3={2,3,5} 就相互认识了,10 个人形成了三个小团体。

假设右三个群主 0, 1, 2 担任队长,负责大家的出行。

一趟火车之旅后,每个小分队成员就互相熟悉,称为了一个朋友圈。

从上图可以看出:
  • 编号 6, 7, 8 同学属于 0 号小分队,该小分队中有 4 人(包含队长 0);
  • 编号为 4和 9 的同学属于 1 号小分队,该小分队有 3 人(包含队长 1);
  • 编号为 3 和 5 的同学属于 2 号小分队,该小分队有 3 个人(包含队长 1)。
仔细观察数组中内融化,可以得出以下结论:
  1. 数组的下标对应集合中元素的编号
  2. 数组中如果为负数负号代表根数字的绝对值代表该集合中元素个数
  3. 数组中如果为非负数,代表该元素双亲在数组中的下标

2、并查集的应用

在公司工作一段时间后,广州小分队中 8 号同学与深圳小分队 1 号同学奇迹般的走到了一起,两个小圈子的学生相互介绍,最后成为了一个小圈子:

现在 0 集合有 7 个人,2 集合有 3 个人,总共两个朋友圈。

通过以上例子可知,并查集一般可以解决一下问题:
(1)查找元素属于哪个集合
沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)。

(2)查看两个元素是否属于同一个集合
沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在。

(3)将两个集合归并成一个集合
将两个集合中的元素合并将一个集合名称改成另一个集合的名称。

(4)统计集合的个数
遍历数组,数组中元素为负数的个数即为集合的个数。

二、并查集的实现

class UnionFindSet
{
public:UnionFindSet(size_t n):_ufs(n, -1){}// 初始时,将数组中元素全部设置为1void Union(int x1, int x2){int root1 = FindRoot(x1);int root2 = FindRoot(x2);// 如果x1已经与x2在同一个集合就不需要合并了if (root1 == root2)return;// 控制数据量小的往大的集合合并if (abs(_ufs[root1]) < abs(_ufs[root2]))swap(root1, root2);// 将两个集合中元素合并_ufs[root1] += _ufs[root2];// 将其中一个集合名称改变成另外一个_ufs[root2] = root1;}// 给一个元素的编号,找到该元素所在集合的名称int FindRoot(int x){int root = x;// 如果数组中存储的是负数,找到,否则一直继续while (_ufs[root] >= 0){root = _ufs[root];}//路径压缩while (_ufs[x] >= 0){int parent = _ufs[x];_ufs[x] = root;x = parent;}return root;}bool InSet(int x1, int x2){return FindRoot(x1) == FindRoot(x2);}// 数组中负数的个数,即为集合的个数size_t SetSize(){size_t size = 0;for (size_t i = 0; i < _ufs.size(); i++){if (_ufs[i] < 0){size++;}}return size;}private:vector<int> _ufs;
};

http://www.ppmy.cn/embedded/21373.html

相关文章

conda环境导出环境内的包(requirements.txt)

问题 跑代码的时候配置环境是一个很麻烦的问题&#xff0c;一个项目可能需要很多包&#xff0c;可以使用pip/conda导出conda虚拟环境中的包。 解决 方式一&#xff1a;使用pip 1.使用pip freeze pip freeze > requirements.txt #可能会丢失依赖包的版本号 # 或者 pip l…

java IO模型详解

一、I/O中的同步、异步、阻塞和非阻塞 在计算机编程和系统设计中&#xff0c;同步&#xff08;Synchronous&#xff09;和异步&#xff08;Asynchronous&#xff09;以及阻塞&#xff08;Blocking&#xff09;和非阻塞&#xff08;Non-blocking&#xff09;是描述程序在执行IO…

【数据结构】排序

参考&#xff1a; 图解算法数据结构 leetcode题解 How to choose&#xff1a;如果对时间复杂度要求比较高并且键的分布范围比较广&#xff0c;可以使用归并排序、快速排序和堆排序。如果不能使用额外的空间&#xff0c;那么快速排序和堆排序都是不错的选择。如果规定了排序的键…

RN传入数字返回拼音首字母的包

安装包 yarn add pinyin^3.0.1 我当前项目的版本 切记不要安装4.0.0-alpha.0 这种版本号后面带字母的,会有问题 import React, {useEffect} from react; import {View, Text} from react-native; import {pinyin} from pinyin;const App () > {// 定义一个函数来将汉字转为…

编译支持播放H265的cef控件

接着在上次编译的基础上增加h265支持编译支持视频播放的cef控件&#xff08;h264&#xff09; 测试页面&#xff0c;直接使用cef_enhancement,里边带着的那个html即可&#xff0c;h265视频去这个网站下载elecard,我修改的这个版本参考了里边的修改方式&#xff0c;不过我的这个…

计算机网络组成—物理层

一、物理层基本概念 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的传输媒体。 1物理层接口特性 机械特性&#xff1a;定义物理连接的特性&#xff0c;规定物理连接时所采用的规格、接口形状、引线数目、引脚数量和排列情况电气特性&…

C++—DAY2

定义一个矩形类Rec&#xff0c;包含私有属性length&#xff0c;width&#xff0c;有以下成员函数: void set length(int l);//设置长度 void set width(int w); //设置宽度 int get length(); //获取长度 int get_width(); //获取宽度 void show(); //输出…

关于远程桌面端口的优化措施的建议

在信息技术的世界中&#xff0c;远程桌面连接已成为企业、教育和个人用户之间共享信息、协作工作的重要工具。而这一切的背后&#xff0c;都离不开远程桌面端口&#xff08;RDP&#xff0c;Remote Desktop Protocol Port&#xff09;的支持。RDP端口不仅关乎到远程访问的顺畅性…