并查集(Union-Find)用于处理不相交集合的合并与查询:Union(合并两个集合)、Find(查找元素所属集合)。
int UFSets[Size]; // 下标对应元素,值对应双亲
void Init(int S[], int n){
for (int i = 0; i < n; i++) S[i] = -1; // -1 表示根结点
}
// Find:沿双亲上溯到根,O(h)
int Find(int S[], int x){
while (S[x] >= 0) x = S[x];
return x;
}
// Union:把 Root2 挂到 Root1 下,O(1)
void Union(int S[], int Root1, int Root2){
S[Root2] = Root1;
}
1. 按秩合并(Union by Rank):将矮树合并到高树下,避免树过高。
void Union(int S[], int Root1, int Root2){
if (Root1 == Root2) return;
if (S[Root2] < S[Root1]) S[Root1] = Root2; // Root2 更深
else {
if (S[Root1] == S[Root2]) S[Root1]--; // 高度加 1
S[Root2] = Root1;
}
}
2. 路径压缩(Path Compression):Find 时将沿途结点直接挂到根下。
int Find(int S[], int x){ // 递归版
if (S[x] < 0) return x;
S[x] = Find(S, S[x]); // 路径压缩
return S[x];
}
3. 两者结合:按秩合并 + 路径压缩,均摊 O(α(n))(α 为反阿克曼函数,近似 O(1))。
初始 $S = [-1,-1,-1,-1,-1,-1,-1]$,依次 Union:
| 操作 | S 数组变化 | 说明 |
|---|---|---|
| Union(0,1) | [-1, 0, -1, -1, -1, -1, -1] | 1 挂到 0 下 |
| Union(2,3) | [-1, 0, -1, 2, -1, -1, -1] | 3 挂到 2 下 |
| Union(4,5) | [-1, 0, -1, 2, -1, 4, -1] | 5 挂到 4 下 |
| Union(0,2) | [-1, 0, 0, 2, -1, 4, -1] | 2 挂到 0 下 |
| Union(0,4) | [-1, 0, 0, 2, 0, 4, -1] | 4 挂到 0 下 |
当前集合:{0,1,2,3,4,5}、{6}
$S=[-1,0,0,2,0,4,-1]$:Find(5):$S[5]=4 \geq 0 \to x=4$;$S[4]=0 \geq 0 \to x=0$;$S[0]=-1 < 0$ 返回 0。Find(3):3→2→0 返回 0。
Find(3) 后 3→2→0 压缩为:3、2 都直接指向 0,S 变为 $[-1,0,0,0,0,4,-1]$。
| 操作 | 基本实现 | 优化后 |
|---|---|---|
| Find | O(h) | O(α(n)) |
| Union | O(1) | O(1) |
| 空间 | O(n) | O(n) |
应用场景:判断图的连通性;Kruskal 算法中判断是否形成环;等价类问题。
↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。