并查集介绍

参考了 《挑战程序设计竞赛》 第二版 P88

  并查集可以高效地进行如下操作:

  • 查询元素a和元素b是否属于同一组
  • 合并a和b所在的组

  下面看下并查集常用方法init, find, unit, same的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int par[MAX_SIZE], rank[MAX_SIZE]; // par代表父节点,rank代表高度
void init(int n) { // 初始化并查集,父节点就是自己,高度是0
for (int i = 0; i < n; i++) {
par[i] = i;
rank[i] = 0;
}
}
int find(int x) {
if(par[x] == x) {
return x;
} else {
return par[x] = find(par[x]);// 在查询的过程中就进行了路径压缩
}
}
void unit(int x, int y) {
x = find(x);
y = find(y);
if(x == y) {
return;
}
if(rank[x] < rank[y]) { // x所在的树的高度如果小于y,那么就把x的父节点设置成y,注意对于y来说所在树的高度并没有增加,所以不需要对rank[y]进行操作
par[x] = y;
} else {
par[y] = x;
if(rank[x] == rank[y]) { // 如果相等,那么把x所在的树的高度加一,这里rank不是真正高度只是相对权重。
rank[x]++;
}
}
}
bool same(int x, int y) {
return find(x) == find(y);
}

  加上了路径压缩和树的高度rank数组的优化,一次操作的复杂度是O(alpha(n)),alpha(n)是Ackermann函数的反函数,比O(log(n))还要快。