首页/数据结构/04-tree/并查集 🔗 在 Obsidian 中打开
数据结构 · 04-tree

并查集

重要度 ⭐⭐⭐ 并查集集合合并查找
速查
数组存双亲下标(-1 表示根)。Find:沿 parent 上溯到根,O(h);Union:把一棵根挂到另一棵根下,O(1)。优化:按秩合并(矮树挂高树)+ 路径压缩(Find 时沿途直接挂根)→ 均摊 O(α(n))

核心概念

并查集(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;
    }
}
秩的含义S[i] 存储高度的负值(根结点),所以 S[i] 越小,树越高。

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))。

手算示例

例 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}

例 2:Find 操作

$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。

例 3:路径压缩

Find(3) 后 3→2→0 压缩为:3、2 都直接指向 0,S 变为 $[-1,0,0,0,0,4,-1]$。

常见考法

考法 1给出一系列 Union/Find 操作,模拟数组变化。
考法 2 · 连通性两个元素是否在同一集合?答:Find(x) == Find(y)。
考法 3执行若干 Union 后有多少连通分量?答:根结点数(S[i] < 0 的个数)。
考法 4 · 复杂度路径压缩 + 按秩合并的时间复杂度?答:均摊 O(α(n)),近似 O(1)。

易错点

易错清单
  1. S 数组存储的是双亲下标:不是高度。
  2. 按秩合并时 S[i] 是高度的负值:S[i] 越小树越高。
  3. 路径压缩在 Find 时执行:不是 Union 时。
  4. Union 两个根结点:不能 Union 非根结点。
  5. 合并方向:小树合并到大树下。

核心结论

操作基本实现优化后
FindO(h)O(α(n))
UnionO(1)O(1)
空间O(n)O(n)

应用场景:判断图的连通性;Kruskal 算法中判断是否形成环;等价类问题。

记忆卡片

并查集支持哪两种操作?
Union(合并)和 Find(查找)。
用什么存储结构?
数组:下标对应元素,值对应双亲(-1 表示根)。
路径压缩的作用?
把沿途结点直接挂到根下,降低树高。
如何判断同一集合?Kruskal 中作用?
Find(x)==Find(y);Kruskal 中判断加边是否会成环。

交互动画 · Union / Find / 路径压缩

S 数组(左,值=双亲下标,-1=根)与集合树(右) 下标S[i] 0-1 1-1 2-1 3-1 4-1 5-1 6-1 0 1 2 3 4 5 6 6 自成集合
初始:每个元素自成集合(S[i]=-1)
点「Union · 下一步」执行 5 次合并,或演示 Find / 路径压缩
S[i] 存双亲下标、-1 为根;按秩合并 + 路径压缩后均摊 O(α(n))。

相关知识点

tree-and-forest tree-storage-structure

↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。