并查集知识学习

(转)

并查集的作用:并和查,即合并和查找,将一些集合合并,快速查找或判断某两个集合的关系,或某元素与集合的关系,或某两个元素的关系。

并查集的结构:并查集主要操作对象是森林,树的结构赋予它独特的能力,对整个集合操作转换为对根节点(或称该集合的代表元素)的操作,一个集合里的元素关系不一定确定,但相对于根节点的关系很明了,这也是为了查找方便。

并查集优化方法:按秩合并和路径压缩的配合使用,使得查找过程优化到极致。按秩合并,每次将深度小的树合并到深度大的树里面去,使得整棵树尽量矮;路径压缩,将当前节点到根节点路径上的所有点直接连到根节点上,使得每个点到根节点的距离更短,在下一次查找的时候更快。

如何快速确定偏移量公式:

例:现在要合并节点x,y, 找到根节点fx = Find(x); fy = Find(y);一般情况下,根节点的偏移量都保持为0, offset[foot] = 0;如果要使得x和y的偏移量为t,假设fx指向fy,则可以写出公式offset[x] + offset[fx] - offset[y] = t,则offset[fx] = (offset[y] + t - offset[x]) % n; 这个n即为总共有多少类,如:在poj1182 食物链中n = 3,,在poj2492 A Bug‘s Life中n = 2, 这样fx的偏移量就计算出来了,只需要改其中一个根节点的偏移量,这里是fx,因为假设是fx指向fy。

非递归路劲压缩:

 1 代码
 2
 3 int Find(int x){
 4     int r = x;
 5     while (r != bin[r]){
 6           r = bin[r];
 7     }
 8     int y = x;
 9     while (y != bin[y]){
10           y = bin[y];
11           bin[y] = r;
12     }
13     return r;
14 }

递归式路径压缩:

1 int Find(int x){
2     if (x != bin[x]){
3        return bin[x] = Find(bin[x]);
4     }
5     return x;
6 }

部分并查集题:

poj1611 The Suspects  题解

poj2492 A Bug‘s Life  题解

poj1182 食物链  题解

hdu1558 Segment set  题解

hdu1198 Farm Irrigation  题解

三、并查算法

通过对上面引题的分析,我们已经十分清楚——所谓并查集算法就是对不相交集合(disjoint set)进行如下两种操作:

(1)检索某元素属于哪个集合;

(2)合并两个集合。

我们最常用的数据结构是并查集的森林实现。也就是说,在森林中,每棵树代表一个集合,用树根来标识一个集合。有关树的形态在并查集中并不重要,重要的是每棵树里有那些元素。

1. 合并操作

为了把两个集合S1和S2并起来,只需要把S1的根的父亲设置为S2的根(或把S2的根的父亲设置为S1的根)就可以了。

这里有一个优化:让深度较小的树成为深度较大的树的子树,这样查找的次数就会少些。这个优化称为启发式合并。可以证明:这样做以后树的深度为O(logn)。即:在一个有n个元素的集合,我们将保证移动不超过logn次就可以找到目标。

【证明】我们合并一个有i个结点的集合和一个有j个结点的集合,我们设i≤j,我们在一个小的集合中增加一个被跟随的指针,但是他们现在在一个数量为i+j的集合中。由于:

1+log i=log(i+i)<=log(i+j);

所以我们可以保证性质。

由于使用启发式合并算法以后树的深度为O(logn),因此我们可以得出如下性质:启发式合并最多移动2logn次指针就可以决定两个事物是否想联系。

同时我们还可以得出另一个性质:启发式快速合并所得到的集合树,其深度不超过 ,其中n是集合S中的所有子集所含的成员数的总和。

【证明】我们可以用归纳法证明:

当i=1时,树中只有一个根节点,即深度为1

又|log2 1|+1=1所以正确。

假设i≤n-1时成立,尝试证明i=n时成立。

不失一般性,可以假设此树是由含有m(1≤m≤n/2)个元素,根为j的树Sj,和含有n-m个元素、根为k的树Sk合并而得到,并且,树j合并到树k,根是k。

(1)若合并前:子树Sj的深度<子树Sk的深度

则合并后的树深度和Sk相同,深度不超过:

|log2(n-m)|+1

显然不超过|log2 n|+1;

(2)若合并前:子树Sj的深度≥子树Sk的深度

则合并后的树的深度为Sj的深度+1,即:

(|log2m|+1)+1=|log2(2m)|+1<=|log2n|+1

小结:实践告诉我们,上面所陈述的性质对于一个m条边n个事物的联系问题,最多执行mlogn次指令。我们只是增加了一点点额外的代码,我们就把程序的效率很大地提升了。大量的实验可以告诉我们,启发式合并可以在线形时间内解答问题。更确切地说,这个算法运行时间的花费,很难再有更加明显的优秀、高效的算法了。

2. 查找操作

查找一个元素u也很简单,只需要顺着叶子到根结点的路径找到u所在的根结点,也就是确定了u所在的集合。

这里又有一个优化:找到u所在树的根v以后,把从u到v的路径上所有点的父亲都设置为v,这样也会减少查找次数。这个优化称作路径压缩(compresses paths)。

压缩路径可以有很多种方法,这里介绍两种最常用的方法:

(1)满路径压缩(full compresses paths):这是一种极其简单但又很常用的方法。就是在添加另一个集合的时候,把所有遇到的结点都指向根节点。

(2)二分压缩路径(compresses paths by halving):具体思想就是把当前的结点,跳过一个指向父亲的父亲,从6而使整个路径减半深度减半。这种办法比满路径压缩要快那么一点点。数据越大,当然区别就会越明显。

压缩路径的本质使路径深度更加地减小,从而使访问的时候速度增快,是一种很不错的优化。在使用路径压缩以后,由于深度经常性发生变化,因此我们不再使用深度作为合并操作的启发式函数值,而是使用一个新的rank数。刚建立的新集合的rank为0,以后当两个rank相同的树合并时,随便选一棵树作为新根,并把它的rank加1;否则rank大的树作为新根,两棵树的rank均不变。

3. 时间复杂度

并查集进行n次查找的时间复杂度是O(n )(执行n-1次合并和m≥n次查找)。其中 是一个增长极其缓慢的函数,它是阿克曼函数(Ackermann Function)的某个反函数。它可以看作是小于5的。所以可以认为并查集的时间复杂度几乎是线性的。

通过上面的分析,我们可以得出:并查集适用于所有集合的合并与查找的操作,进一步还可以延伸到一些图论中判断两个元素是否属于同一个连通块时的操作。由于使用启发式合并和路径压缩技术,可以讲并查集的时间复杂度近似的看作O(1),空间复杂度是O(N),这样就将一个大规模的问题转变成空间极小、速度极快的简单操作。

并查集模板

 1 1、make_set(x) 把每一个元素初始化为一个集合
 2 建立一个新的集合,其中集合只有唯一的一个元素x
 3
 4 2、union_set(x, y) 按秩合并x,y所在的集合
 5
 6 3、find_set(x)返回x所在的集合的代表
 7
 8    
 9    在执行查找操作时,要沿着父节点指针一直找下去,直到找到树根为止。大家要注意途中的箭头。
10 4、实现并查集的标准代码:
11  1 #include <stdio.h>
12  2
13  3 const int MAXN = 100; /*结点数目上线*/
14  4 int pa[MAXN];    /*p[x]表示x的父节点*/
15  5 int rank[MAXN];    /*rank[x]是x的高度的一个上界*/
16  6
17  7 void make_set(int x)
18  8 {/*创建一个单元集*/
19  9     pa[x] = x;
20 10     rank[x] = 0;
21 11 }
22 12
23 13 int find_set(int x)
24 14 {/*带路径压缩的查找*/
25 15     if(x != pa[x])
26 16         pa[x] = find_set(p[x]);
27 17     return pa[x];
28 18 }
29 19
30 20 /*按秩合并x,y所在的集合*/
31 21 void union_set(int x, int y)
32 22 {
33 23     x = find_set(x);
34 24     y = find_set(y);
35 25     if(rank[x] > rank[y])/*让rank比较高的作为父结点*/
36 26         pa[y] = x;
37 27     else
38 28     {
39 29         pa[x] = y;
40 30         if(rank[x] == rank[y])
41 31             rank[y]++;
42 32     }
43 33 }

时间: 2024-08-03 13:50:29

并查集知识学习的相关文章

并查集——新手学习记录

好吧,什么垃圾并查集,并查集什么的都是铁憨憨<+__+> 现在开始复习回忆:(新手,有错误望指正) 什么叫做并查集,并查集就是一个集合问题,其实最主要的就是知道并查集是一个求解集合数目的问题,具体的操作方法有点飘. 或者这样理解:——并查集通过一个一维数组来实现,其本质是维护一个森林.(好吧,我也不是很理解),我的理解就是通过一维数组来实现,子节点与父节点之间联系,然后查找集合个数....... 好吧,不清楚,如果看了前面你很懵逼,那就全都忘了吧,,, 接下来才是正餐:https://blog

hdoj 1325 Is It A Tree? 【并查集】

做了一上午,终于ac了 wa了一次主要是忘了还有环!!! 主要是运用并查集知识,又复习了一次!! 思路:输入之后找能不能成环,成环就不是,其次还要判断是不是有两个父节点,如果有两个父节点也不是,之后就找相关的祖先就好了: 还要注意:如果只有一个节点,也是树,如果有两个或多个根节点也不是树:如果没有根节点也不是 链接http://acm.hdu.edu.cn/showproblem.php?pid=1325 代码 #include<stdio.h> int fat[1000]; int fath

并查集练习(0743) SWUST OJ

#include<iostream> #include<cstring> using namespace std; int a[1000005]; int n,m,l,ci,di; int root(int x) //找到根节点 { int r = x; while(r != a[r]) r = a[r]; int i = x,j; while(i != r) //压缩路径 { j = a[i]; a[i] = r; i = j; } return r; } void mix(in

算法 - 并查集

并查集 - 知乎专栏 POJ 1611(并查集+知识) POJ 2524(并查集) 这个时候我们就希望重新设计一种数据结构,能够高效的处理这三种操作,分别是 MAKE-SET(x),创建一个只有元素x的集合,且x不应出现在其他的集合中 UNION(x, y),将元素x所在集合Sx和元素y所在的集合Sy合并,这里我们假定Sx不等于Sy FIND-SET(x),查找元素x所在集合的代表 并查集的具体实现. 对于有根树或者说森林的表示,可以用一个数组parent来实现. parent[i]记录元素i的

并查集学习

从HOJ1232来学习并查集 #include<iostream> #include<vector> using namespace std; //封装好的并查集 class UF{//union find private: vector<int> v;//存储各个结点 v[i]=j 表示 第i个结点的代表元是j(所在树的根结点) public: //初始化 UF(int n){//初始化 总共有n个结点 刚开始的时候v[i]=i for(int i=0;i<=n

并查集--学习详解

[转] 文章作者:yx_th000 文章来源:Cherish_yimi (http://www.cnblogs.com/cherish_yimi/) 昨天和今天学习了并查集和trie树,并练习了三道入门题目,理解更为深刻,觉得有必要总结一下,这其中的内容定义之类的是取自网络,操作的说明解释及程序的注释部分为个人理解.并查集学习: l         并查集:(union-find sets) 一种简单的用途广泛的集合. 并查集是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很

算法学习——动态图连通性(线段树分治+按秩合并并查集)

在考场上遇到了这个的板子题,,,所以来学习了一下线段树分治 + 带撤销的并查集. 题目大意是这样的:有m个时刻,每个时刻有一个加边or撤销一条边的操作,保证操作合法,没有重边自环,每次操作后输出当前图下所有联通块大小的乘积. 首先观察到如果没有撤销操作,那么直接用并查集就可以维护,每次合并的时候乘上要合并的两个并查集大小的逆元,然后乘上合并之后的大小即可. 那么来考虑撤销,观察到如果并查集不带路径压缩,应该是可以处理撤销操作的. 但我们并不能直接做,因为并查集的撤销必须按顺序来,就相当于每次合并

【算法学习笔记】41.并查集 SJTU OJ 1283 Mixture

---恢复内容开始--- Description CC非常喜欢化学,并且特别喜欢把一大堆液体倒在一起. 现在CC有n种液体,其中m对会发生反应,现在她想把这n种液体按某种顺序倒入一个容器内,让她获得最刺激的体验,使危险系数尽量大. 我们可以这样计算危险系数,一开始容器内没有任何液体,危险系数为1.每次液体倒入容器时,若容器内已有一种或多种液体会与这种液体发生反应,则危险系数会乘2,否则危险系数不变. 请你求出把这n种液体倒在一起的最大危险系数. Input Format 第一行为两个数n和m.

【日常学习】【并查集+map】codevs2639 约会计划题解

然而我居然让诸城一中悲剧机房的C++可以编译了··· 直接上题目 题目描写叙述 Description cc是个超级帅哥,口才又好.rp极高(这句话似乎降rp),又非常的幽默,所以非常多mm都跟他关系不错. 然而.最关键的是,cc可以非常好的调解各各妹妹间的关系.mm之间的关系及其复杂.cc必须严格掌握她们之间的朋友关系,好一起约她们出去,cc要是和不是朋友的两个mm出去玩.后果不堪设想-- cc仅仅掌握着一些mm之间的关系.可是cc比較聪明.他知道a和b是朋友,b和c 是朋友,那么a和c也是朋