并查集是一种树型的数据结构,其保持着用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。

有一个联合-查找算法union-find algorithm)定义了两个操作用于此数据结构:

  • Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
  • Union:将两个子集合并成同一个集合。
下面代码实现一些并查集中的一些基本操作:
一、并查集的初始化
假设现在有一个全集合为S= {0,1,2,3,4,5,6,7,8,9} ,初始化时,将每一元素都划分成为一个单元素的集合。
如下图所示:
这样就初始化出10个单元素集合,每一个集合元素的parent值都是-1.
代码实现过程:
[cpp] view plaincopy
  1. int parent[100];
  2. //构造一个初始并查集,s是集合元素的个数,此时初始化了s个集合,每一个集合都只有一个元素
  3. //数组的范围为parent[0]~parent[s-1]
  4. //初始的时候,数组元素的值都是 -1,表示此时都是根结点。
  5. void ufset(int s)
  6. {
  7. int si=s;
  8. for(int i=0;i<si;i++)
  9. parent[i]=-1;
  10. }

二、合并Union

所谓合并,即是将两个不相交的集合合并成为一个集合,这个过程将一个集合的parent修改成另一集合的parent。
代码实现过程:
[cpp] view plaincopy
  1. //集合的合并
  2. //让root2的父指针指向root1即可实现两个集合的合并
  3. void Union(int root2,int root1)
  4. {
  5. parent[root1]+=parent[root2];
  6. parent[root2]=root1;
  7. }

举个例子:初始化10个元素(如上图所示),进行下面的合并过程:

[cpp] view plaincopy
  1. ufset(10);  //初始化10个元素
  2. Union(6,0);
  3. Union(7,0);
  4. Union(8,0);
  5. Union(4,1);
  6. Union(9,1);
  7. Union(3,2);
  8. Union(5,2);

合并之后的结果:

但是进行这种合并之后,可能会出现一种不好的效果。如下图:
我们称之为一棵退化的树。
那么如何进行改进呢?我们可以使用加权规则进行合并。也就是将集合元素少的归到集合元素多的中去。
例如:
下面给出代码实现:(注意下面这个方法存在错误,请看下面第二个方法,已修正错误。)
[cpp] view plaincopy
  1. //使用加权规则得到改进的Union操作
  2. void WeightUnion(int root2,int root1)
  3. {
  4. int r2=Find(root2);  //r2和r1是root2和root1的父结点
  5. int r1=Find(root1);
  6. int temp;
  7. if(r1!=r2)
  8. {
  9. temp=parent[r1]+parent[r2];
  10. if(parent[r1]<parent[r2]){parent[r1]=temp;parent[r2]=r1;}  //以r2根的树结点多
  11. else {parent[r1]=r2;parent[r2]=temp;}
  12. }
  13. }
错误提醒:很感谢某位网友指出我上面段代码的错误,由于一时疏忽上面WeightUnion这个方法有点错误!修改如下:
[cpp] view plaincopy
  1. //使用加权规则得到改进的Union操作
  2. void WeightUnion(int root2,int root1)
  3. {
  4. int r2=Find(root2);  //r2和r1是root2和root1的父结点
  5. int r1=Find(root1);
  6. int temp;
  7. temp=parent[r1]+parent[r2];
  8. if(parent[r1]<=parent[r2])
  9. //注意:parent[r1] 和 parent[r2] 都是负数,所以小者其绝对值大,那么拥有的结点数就多
  10. //这里就说明 r1为根的树结点  不少于  r2为根的树结点
  11. {
  12. parent[r1]=temp;    //以r1为根
  13. parent[r2]=r1;
  14. }
  15. else
  16. {
  17. parent[r1]=r2;
  18. parent[r2]=temp;
  19. }
  20. }
三、查找Find
我们知道,只有当查找到的元素的parent值为负数(此时集合元素个数用这个负数表示),才表示找到根。
[cpp] view plaincopy
  1. //查找元素x所在集合
  2. //从x开始,沿父指针链一直向上,直到向上,直到达到一个父指针域为负值的结点位置
  3. int Find(int x) //迭代查找方式
  4. {
  5. while(parent[x]>=0) x=parent[x];
  6. return x;
  7. }
  8. /*
  9. int find_1(int x)  //递归查找方式
  10. {
  11. if(parent[x]<0) return x;  //x是根时,直接返回x
  12. else return find_1(parent[x]);  //否则,递归找x的父的根
  13. }
  14. */

进一步优化,在查找过程中可以采用折叠规则压缩路径。

实现的代码:
[cpp] view plaincopy
  1. //折叠规则压缩路径法
  2. //包含元素i的树中搜索根,并将从元素i到根的路径上的所有结点都变成根的结点
  3. int collapsingfind(int i)
  4. {
  5. int j;
  6. for(j=i;parent[j]>=0;j=parent[j]); //搜索j的根
  7. while(i!=j)   //向上逐次压缩
  8. {
  9. int temp=parent[i];
  10. parent[i]=j;
  11. i=temp;
  12. }
  13. return j;  //返回根
  14. }
附:上面代码的完整版:
[cpp] view plaincopy
  1. #include <iostream>
  2. using namespace std;
  3. int parent[100];
  4. //构造一个初始并查集,s是集合元素的个数,此时初始化了s个集合,每一个集合都只有一个元素
  5. //数组的范围为parent[0]~parent[s-1]
  6. //初始的时候,数组元素的值都是 -1,表示此时都是根结点。
  7. void ufset(int s)
  8. {
  9. int si=s;
  10. for(int i=0;i<si;i++)
  11. parent[i]=-1;
  12. }
  13. //查找元素x所在集合
  14. //从x开始,沿父指针链一直向上,直到向上,直到达到一个父指针域为负值的结点位置
  15. int Find(int x) //迭代查找方式
  16. {
  17. while(parent[x]>=0) x=parent[x];
  18. return x;
  19. }
  20. /*
  21. int find_1(int x)  //递归查找方式
  22. {
  23. if(parent[x]<0) return x;  //x是根时,直接返回x
  24. else return find_1(parent[x]);  //否则,递归找x的父的根
  25. }
  26. */
  27. //折叠规则压缩路径法
  28. //包含元素i的树中搜索根,并将从元素i到根的路径上的所有结点都变成根的结点
  29. int collapsingfind(int i)
  30. {
  31. int j;
  32. for(j=i;parent[j]>=0;j=parent[j]); //搜索j的根
  33. while(i!=j)   //向上逐次压缩
  34. {
  35. int temp=parent[i];
  36. parent[i]=j;
  37. i=temp;
  38. }
  39. return j;  //返回根
  40. }
  41. //集合的合并
  42. //让root2的父指针指向root1即可实现两个集合的合并
  43. void Union(int root2,int root1)
  44. {
  45. parent[root1]+=parent[root2];
  46. parent[root2]=root1;
  47. }
  48. //使用加权规则得到改进的Union操作
  49. void WeightUnion(int root2,int root1)
  50. {
  51. int r2=Find(root2);  //r2和r1是root2和root1的父结点
  52. int r1=Find(root1);
  53. int temp;
  54. if(r1!=r2)
  55. {
  56. temp=parent[r1]+parent[r2];
  57. if(parent[r1]<parent[r2]){parent[r1]=temp;parent[r2]=r1;}  //以r2根的树结点多
  58. else {parent[r1]=r2;parent[r2]=temp;}
  59. }
  60. }
  61. int main()
  62. {
  63. ufset(10);  //初始化10个元素
  64. Union(6,0);
  65. Union(7,0);
  66. Union(8,0);
  67. Union(4,1);
  68. Union(9,1);
  69. Union(3,2);
  70. Union(5,2);
  71. for(int i=0;i<10;i++) cout<<parent[i]<<" ";
  72. return 0;
  73. }

数据结构 之 并查集相关推荐

  1. 数据结构-PHP 并查集(Union Find)

    文章目录 数据结构-PHP 并查集(Union Find) 1.并查集示意图 2.并查集合并 3.并查集简单的代码示例 3.1 PHP代码定义 3.2 输出演示 数据结构-PHP 并查集(Union ...

  2. 数据结构之并查集Union-Find Sets

    1.  概述 并查集(Disjoint set或者Union-find set)是一种树型的数据结构,常用于处理一些不相交集合(Disjoint Sets)的合并及查询问题. 2.  基本操作 并查集 ...

  3. 数据结构之并查集:并查集的介绍与Python代码实现——18

    并查集的介绍 并查集(Union-find)数据结构也称作合并查找集(Merge-find set)或者不相交集数据结构(disjoint-set data structure),它是一种记录了由一个 ...

  4. 数据结构 之 并查集(Disjoint Set)

    一.并查集的概念:     首先,为了引出并查集,先介绍几个概念:     1.等价关系(Equivalent Relation)     自反性.对称性.传递性.     假设a和b存在等价关系.记 ...

  5. 数据结构 7并查集(DISJOINT SET)

    并查集(The disjoint set ADT) 等价关系 Relation R:若对于每一对元素(a,b),a,b∈S,aRb或者为true或者为false,则称集合S上定义关系R.如果aRb为t ...

  6. [重修数据结构0x03]并查集、堆、优先队列(2021.8.11)

    前言 在做遍历的题目的时候,发现掌握一些特殊的数据结构和技巧有时对解决题目有着决定性的作用,不可不学.因此特地拿出来两天学习一下并查集.堆.优先队列.以后有更多思考和感悟再加补充吧.内容来自算法笔记, ...

  7. 高阶数据结构(1):并查集 与 图

    "Head in the clouds" 一.并查集 (1)认识并查集? 在一些问题中需要将n个不同的元素划分成 一些不想交的集合. 开始时,每个元素自成一个单元素集合,然后按一定 ...

  8. 【高级数据结构】并查集

    目录 A.AcWing 1250. 格子游戏 B.AcWing 1252. 搭配购买 C.AcWing 237. 程序自动分析 D.AcWing 239. 奇偶游戏 E.AcWing 238. 银河英 ...

  9. 0x41.数据结构进阶 - 并查集

    目录 一.路径压缩与按秩合并 1.AcWing 237. 程序自动分析(NOIP2015) 二.边带权并查集 1.AcWing 238. 银河英雄传说(边带权并查集模板) 2.AcWing 239. ...

最新文章

  1. 头条三面:toString()、String.valueOf、(String)强转,有啥区别?
  2. Hadoop的TextInputFormat的作用,如何自定义实现的
  3. mysql通过函数完成10的阶乘_请使用函数的递归调用编写求阶乘的函数,并计算1!+2!+3!+4!+5!...
  4. select事件有哪些_Android 深入底层:Linux事件管理机制 epoll
  5. GDB中应该知道的几个调试方法-转
  6. Selenium常见异常分析及解决方案
  7. 【贪心School】机器学习课程笔记
  8. String、StringBuiler、StringBuffer的区别
  9. jQuery特效:实现抽奖
  10. 为什么32.768kHz的晶振封装这么另类?
  11. wgs84坐标系经纬度投影_南方cass坐标转经纬度_坐标转经纬度软件
  12. 互联网产品经理核心素质能力模型
  13. java解密加密MD5
  14. 阿里云远程桌面连接不到的问题
  15. 用数据说话,Python就业前景分析
  16. LZY碎大石(YYOJ)(优先队列
  17. 新绝代双骄三常见问题解答(不同于网上那片)
  18. 【*夜深了,无心睡眠*】
  19. 小鱼征服鼠标宏—第一天—获取鼠标x,y坐标实时动态
  20. 物联网是如何让世界变得更好

热门文章

  1. 【MATLAB】数据类型 ( 矩阵 | 随机数函数 | 生成矩阵 )
  2. 【OpenGL】十一、OpenGL 绘制多个点 ( 绘制单个点 | 绘制多个点 )
  3. 【计算理论】可判定性 ( 可判定性总结 )
  4. POJ 3356 水LCS
  5. python成长之路——第四天
  6. Cocos2d-x 3.0游戏开发之虚拟机IOS环境:匹配才是好,莫要随便升级软件
  7. Microservice Anti-patterns
  8. 关于java中的不可变类(转)
  9. 【转】每天一个linux命令(39):grep 命令
  10. [转载]明天的数字营销分析工具2