首页 > 技术文章 > 带权并查集

bianjunting 2019-04-17 13:49 原文

  学习带权并查集之前我们需要先对并查集和路径压缩压缩了解,有需求的可以参考这篇博客

刚昨天总结了并查集的相关操作,今天做题的时候居然发现自己一直都是存在一些想不到的地方,总是会存在一些漏洞,最骚的是今天做到了食物链这道题......才知道了带权并查集和种类并查集......好了接下

来就要进入带权并查集了。

  上面的博客中有写到路径压缩的相关操作,我们知道路径压缩的结果就是同一集合中的所有元素统一指向树根,这样可以加快访问速度。和路径压缩一样,带权并查集仅仅是在每个集合元素都指向树根

的前提下赋予这些结点相对于树根的权值。

  那么我们就需要用一个数组一个元素相对于他所属集合树根的权值,我们用value存储。

  我们已经知道了基本并查集的一般实现,但是如果加入了value要如何实现呢。

  

  我们思考:

    在find函数中,我们对于每个元素进行一次赋值,使得最终所有元素的父亲结点都指向树根结点,那么如果带有权值的话,我们知道每次都会改变一个结点的父亲,知道找到他所属集合的树根,所以

  我们每次也需要更新对应的value值。

    那么合并函数呢,我们知道每次合并我们会将x所属集合的树根px的父亲设置为y所属集合的树根py,这样就可以将两个集合合并,那么我们知道在这过程中我们改变了px的父亲,也就需要改变px对

应他父亲的权值,我们根据下图就可以知道,对于value[px],因为x最终父亲为py,所以显然两条路的权值应该相等,我们就可以得出如下代码。

    并不是所有类型的更新都是下面这种情况,具体情况具体分析,但是思路宝贵。

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 
 5 const int maxn = 10000 + 5;
 6 int head[maxn], value[maxn], rank[maxn];
 7 
 8 int find(int u) {
 9     if(u != head[u]) {
10         int t = head[u];
11         head[u] = find(head[u]);
12         value[u] += value[t];
13     }
14     return head[u];
15 }
16 
17 void Union_Set(int x, int y, int s) {
18     int fx = find(x), fy = find(y);
19     if(fx == fy) return;
20     if(rank[fx] > rank[fy]) {
21         head[fy] = fx;
22         value[fy] = -value[y] + value[x] + s;
23     } else {
24         head[fx] = fy;
25         value[fx] = -value[x] + value[y] + s;
26         if(rank[fx] == rank[fy]) rank[fy] ++;
27     }
28 }
29 
30 int is_same(int x, int y) {
31     return find(x) == find(y);
32 }
33 
34 int main () {
35 
36     return 0;
37 }  

  后续会更新相关题目:针对题目再做详细描述。

推荐阅读