并查集(The disjoint set ADT)

等价关系

Relation R:若对于每一对元素(a,b),a,b∈S,aRb或者为true或者为false,则称集合S上定义关系R。如果aRb为true,那么我们说a与b有关系。

Equivalence relation:等价关系是满足下列三个性质的R:
(1)自反性(reflexive) 对与所有的a∈S,aRa
(2)对称性(symmetric) aRb 当且仅当 bRa
(3)传递性(transitive) 若aRb 且 bRc 则 aRc

动态等价性问题

如果有一个集合S,a,b均为S中的元素,若a与b有关系,则将a,b归于同一个子集中。因此定义一个元素a∈S的等价类(equivalence class)是S的一个子集,它包含所有与a有关系的元素。所以,等价类形成了对S的一个划分:S中的每一个成员恰好出现在一个等价类中。

如果给出一个集合S={1,2,3,4,5,6,7,8,9,10,11,12}和九个等价关系12≡4,3≡1,6≡10,8≡9,7≡4,6≡8,3≡5,2≡11,11≡12。
这样的话我们可以等到三个等价类{2,4,7,11,12},{1,3,5},{6,8,9,10}.
我们如何设计算法实现上面的功能?

假设输入数据最初是N个等价类,1-12都在不同的集合中,每个集合含有一个元素,这些集合不相交。
开始读入关系 a≡b, 如果Find(a)!=Find(b),则Union(Find(a),Find(b)),就是说,一开始读入3与1,发现3,1在不同的集合中,便将他们所在的集合合并,成{1,3};读入6和10,出现{6,10};读入8和9,生成{8,9};读入7和4,生成{7,4};读入6和8,发现6在{6,10}这个集合中,8在{8,9}这个集合中,于是合并这两个集合,变成{6,8,9,10};……一直读到最后一个关系,得到结果。
上面的算法最主要的就两个操作:Find()——判断该元素处于哪一个集合;Union()——将两个集合合并。

Union/Find算法

Find的功能:返回包含给定元素的集合(即等价类)的名字。(重要的是当两个元素属于相同的集合时,Find会返回相同的名字)
Union的功能:添加关系a~b,先执行Find判断a与b是否有关系,如果没有关系,则使用Union运算将a和b分别所属的集合合并为一个新的集合。

基本数据结构

用树来表示每一个集合,树上的每一个元素的那个相同的根可以用来命名所在的集合。(树的集合叫做森林)
假设树被非显式地存储在一个数组中,P[i]表示元素i的父亲,如果i是根,则P[i]=0。

  • P[Element] = the element’s parent
  • Union(X,Y) ==> P[Y] = X;


如图有八个元素,分别位于八个不同的集合,表示8棵树。P[i] = 0 (1<=i<=8)
我们用Union(X,Y)表示将X,Y合并为一个集合。就是让Y节点的根指针指向X树的根节点。这样X和Y两棵树就合并为一棵树,表示位于一个集合。
比如执行Union(5,6) Union(7,8) Union(5,7) 后 变为:

此时:P[6]=5,P[8]=7,P[7]=5; 只需让:P[Y] = X.
对于Find(X),我们返回包含X的树的根(递归,直到搜索到根节点为止),例如Find(8)会返回5,Find(4)返回0

typedef int DisjSet[NumSets + 1];
void SetUnion(DisjSet S, int Root1, int Root2)
{S[Root2] = Root1;
}
int Find(int X,DisjSet S)
{if(S[X] <= 0){return X;}else{return Find(S[X], S);}
}
//或者使用循环实现
int Find(int X,DisjSet S)
{for(;S[X]>0;X=S[X]);return X;
}

注意

  • 使用Union的时候,传入的参数是两个集合对应的根节点。例如将8,5这两个数所在的集合合并时,应该这样Union(S,Find(8),Find(5)),而不是Union(S,8,5),我们总是对树的根节点进行操作。
  • 约定Union(X,Y)后新的根是X,即将Y的根指向X,P[Y]=X

改进Union算法

Union是通过使第二棵树成为第一棵树的子树来完成合并的。

这种合并方式的确定性会导致很深的树生成,就像上图最右边那个一样。这样就会增加Find操作的时间。那么如何改进算法来解决这个问题呢?

方法一:按大小合并(union-by-size)

我们就不要每次都按照同样的方式合并,按大小合并,总让较小的树成为较大的树的子树。比如对先前的图继续进行Union(4,5)操作,按大小合并结果会是:

实现方法是S[Root]=-Size;每棵树的根节点原来是都存储0,现在我们将它用作存储集合的大小的相反数,初始化为-1,即每个树的大小均为1.合并集合时将两个大小相加。

方法二:按高度合并(union-by-height)

另一种想法是按高度求并,Union使得浅的树成为深的树的子树,只有两棵树相等时求并深度才会增加(注意此时高度增加1)。
我们需要数组来存储树的深度,由于每次合并需要比较根节点的深度,同样S[Root] = -Height. 当i表示根时,原来S[i]=0改为表示树高度的负值;当i不表示根时,S[i] = i’s parent,S[i]表示节点i的父亲

//这里假设root1与root2均为根节点
//若要将4,6所在集合合并通常为Union(Find(4),Find(6));
void SetUnion(DisjSet S, SetType Root1, SetType Root2)
{if(S[Root2] < S[Root1]){     //如果Root2更深,就使Root1指向Root2S[Root1] = Root2;      } else{if (S[Root1] == S[Root2]){  //高度相同s[Root1]--;             //将高度加1}S[Root2] = Root1;}
}

深度最多logNlogNlogN,Find操作的运行时间为O(logN)O(logN)O(logN)
进行N次合并与M次查找操作的时间复杂度为O(N+Mlog2N)O(N+Mlog_2N)O(N+Mlog2​N)

改进Find算法(路径压缩)

Union算法总会产生一些最坏的情况,我们可以对Find算法进行改进,使产生的树的深度减小,也就是路径压缩。
设操作为Find(X),路径压缩的效果是,从X到根的路径上的每一个节点,都使它的父节点变成根。

SetType Find(ElementType X, DisjSet S)
{if(S[X] <= 0){return X;}else{return S[X] = Find(S[X], S);   /*集合的根被递归地找到以后,X的父节点就指向它*/}
}
//循环方式实现
SetType Find(ElementType X, DisjSet S)
{for(root = X; S[root] > 0; root = S[root]);//先找到根节点for(trail = X;trail != root; trail = lead){lead = S[trail];S[trail]=root;}return root;
}
  • 注意
    路径压缩与按大小求并完全兼容。
    路径压缩不完全与按高度求并兼容,因为路径压缩改变了树的高度,我们并不清楚如何重新计算高度。此时,可以不去计算(即使高度变化了),这时候每棵树所存储的高度使估计的高度(有时称为秩rank),理论上按秩求并与按大小求并的效率是一样的。

  • 应用 生成迷宫

数据结构 7并查集(DISJOINT SET)相关推荐

  1. C++用并查集Disjoint union实现connected component连通分量(附完整源码)

    C++用并查集Disjoint union实现connected component连通分量 C++用并查集Disjoint union实现connected component连通分量完整源码(定义 ...

  2. C++并查集Disjoint Set(附完整源码)

    C++并查集Disjoint Set 并查集Disjoint Set算法的完整源码(定义,实现,main函数测试) 并查集Disjoint Set算法的完整源码(定义,实现,main函数测试) #in ...

  3. C语言实现并查集(Disjoint set或者Union-find set)(附完整源码)

    实现实现并查集 实现并查集(Disjoint set或者Union-find set)的完整源码(定义,实现,main函数测试) 实现并查集(Disjoint set或者Union-find set) ...

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

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

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

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

  6. 数据结构 之 并查集

    并查集是一种树型的数据结构,其保持着用于处理一些不相交集合(Disjoint Sets)的合并及查询问题. 有一个联合-查找算法(union-find algorithm)定义了两个操作用于此数据结构 ...

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

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

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

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

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

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

最新文章

  1. [转] WINCC教学视频
  2. window python输入路径问题--转义字符
  3. python3 mysql错误 pymysql.err.OperationalError: (2013, 'Lost connection to MySQL server during query')
  4. 云端计算模型的MATLAB仿真与分析
  5. find命令的exec参数使用---Linux学习笔记
  6. HDU 1284 钱币兑换问题 (动态规划 背包方案数)
  7. mybatis是什么_深入解析:Mybatis接口没有实现类为什么可以执行增删改查?
  8. android 8.0 行为变更--day03
  9. epoch如何设置_Kaggle竞赛硬件如何选择?不差钱、追求速度,那就上TPU吧
  10. SimpleDateFormat 类的总结
  11. Windows.h下CONTAINING_RECORD宏解析(转载)
  12. python判断是否为素数的函数 是返回字符串yes_编写函数,判断一个数字是否为素数,是则返回字符串 YES ,否则返回字符串 NO 。_学小易找答案...
  13. ILSpy反编译软件的使用
  14. npn三种波形失真_RF测试笔记:三阶交调失真概述及测试
  15. Flash键盘钢琴谱
  16. word文档中如何将软回车替换为硬回车
  17. 专治不明觉厉:深度解密IBM黑科技量子计算机
  18. CHM文件制作方法及制作中遇到的坑
  19. 通用样式 -表格的每行的复选框选中打印
  20. Hadoop退出安全模式

热门文章

  1. POI中sheet.getRow方法返回值NullPointException
  2. layui-table 多一列问题
  3. 计算机网络使用双绞线连接时,计算机网络技术期末试卷A
  4. 【PAT乙】1085 PAT单位排行 (25分) map排序
  5. 【HDOJ2087】剪花布条(KMP)
  6. 【codevs1163】访问艺术馆
  7. c语言万年历报告ppt,万年历设计报告
  8. JAVA可以赋值分数吗_Java数据类型知多少?
  9. linux 7 打开22端口号,Centos 7 修改 SSH 默认端口号
  10. php xml 格式化,PHP:XML操作