数据结构论坛

首页 » 分类 » 问答 » 高等数据结构之并查集
TUhjnbcbe - 2021/3/1 18:25:00

并查集是一种用互质的集合对数据进行分类管理的数据结构。

并查集主要实现了两个功能:合并与查询

我们用一个数组fa来表示第i个元素所在集合的根节点。

根节点的父节点指向它自身。

初始的时候,我们把fa=i,这样就初始化了n个互质的集合。

然后当要合并两个节点x、y所在的集合的时候,就先找到他们的根节点(代表元),然后将一个集合的根节点指向另一个节点的根节点即可。

判断两个元素所在集合是否相同,其实就是去找他们所在集合的代表元。代表元相同,那么就证明这两个元素在同一个集合里面。

对于题目DSL_1_A来说,题目要求实现一个简单的并查集,代码如下:

对于上面这个题目,我们会发现,运行的状况是这样的

这虽然是AC了,可是耗时有点高啊,耗时0.4s,n的数据范围是,操作数最大为0。这怎么看都不像一个时间复杂度低于O(logn)的算法啊。

那么肯定是有优化空间的。

路径压缩

但是,这样子的话,每次查找都需要递归很多次,非常的费时。我们就可以在合并集合的时候,做路径压缩来解决这个问题。

路径压缩就是,把一个集合里面,正在合并的节点都的父节点都指向合并时的根节点。这样使得路径得到了压缩,减少了查询的耗时。

怎么说吧,我觉得路径压缩有点动态规划的思想在里面,就是每次查询找到当前节点的根节点之后,就更新进去fa数组,然后下次用到这个值的时候,就可以减少调用的次数了。

代码实现如下:

intfind_root(intx){if(fa[x]==x)returnx;intt=find_root(fa[x]);fa[x]=t;returnt;}按秩合并

并查集的按秩合并说白了就是把高度矮的树合并到高度高的树上。

按秩合并能显著降低最长路径长度,这样的话,在查询的时候可以更快地查询到根节点。只有使用了路径压缩+按秩合并的并查集,时间复杂度才会低于O(logn)

我们需要使用一个数组Rank来存储第i个节点作为根节点时,它的树的高度。

那么我们发现,需要更新Rank的情况只有是在合并集合且两个集合树高相等时,才需要把一个集合的根节点的树高+1。

具体的代码实现就是,初始化一个全局数组Rank,把它的值都设为0,接着,把合并集合的函数改成下面这样:

voidunite(intx,inty){intfx=find_root(x);intfy=find_root(y);if(Rank[fx]Rank[fy]){fa[fy]=fx;}else{fa[fx]=fy;if(Rank[fx]==Rank[fy])Rank[fy]++;}}

再把代码提交上去,可以发现,运行时间减少到了0.05s,这差不多加速了10倍啊!

带权并查集

带权并查集就是在并查集的树的连边上附上权值。

带权并查集的合并,需要把权值也加起来。

其实理解并不困难,就是用一个数组s,来存储当前节点到路径压缩后的父节点的权值和。查询的时候,进行路径压缩,并更新s的值。

在合并的时候,需要对新的这条连边赋值,看下面这个图就知道了。

已知:C-A=z

那么根据初中数学知识就可以知道,D-B=y+z-x

接着,给新建立的连边赋值就好了。

龙进

点滴赞赏,弥足珍贵

1
查看完整版本: 高等数据结构之并查集