程序员面试宝典

一站式面试准备平台

返回分类
algorithms中级

并查集

深入理解并查集的数据结构原理、路径压缩和按秩合并的优化、以及在连通性问题中的应用

2026-04-15
阅读时间: 8分钟

并查集

并查集是解决"连通性"问题的利器。关键不是背代码,而是理解为什么路径压缩能让查找接近 O(1)

面试考察点

面试官通过这道题想考察:
1. 你是否理解并查集解决什么问题
2. 你是否能解释路径压缩的原理
3. 你是否能用并查集解决实际问题
4. 你是否理解按秩合并的作用

一、什么是并查集?

1.1 问题引入

面试官追问:"什么是并查集?解决什么问题?"

并查集(Union-Find):处理"连通性"问题的数据结构

连通性问题示例:
- 社交网络:两个人是否相连?
- 朋友圈:有多少个朋友圈?
- 岛屿数量:哪些陆地是连在一起的?

核心操作:
1. Union(A, B):把 A 和 B 合并到同一个集合
2. Find(A):找到 A 所属集合的代表

连通性的特点:
- 自反性:A 和 A 连通
- 对称性:A 和 B 连通 → B 和 A 连通
- 传递性:A 和 B 连通,B 和 C 连通 → A 和 C 连通

1.2 并查集的核心思想

类比:帮派

init:每个人是自己帮派的帮主
Union(A, B):把两个帮派合并
Find(A):找到 A 的帮主

        帮派 1          帮派 2
       帮主 A          帮主 D
      /    \          /    \
     B      C        E      F

Union(A, D):
帮主 A 和帮主 D 打一架
输的认赢的当帮主

        帮派 1+2
       帮主 A
      /  |  \
     B   C   D
            /  \
           E    F

Find(B) = A,表示 B 在 A 的帮派里
Find(E) = A,表示 E 也在 A 的帮派里
所以 B 和 E 是连通的!

二、并查集的实现

2.1 基本实现

并查集用数组实现:

parent[i] = i 的父节点

- 如果 parent[i] = i,说明 i 是根节点
- 通过 parent[i] 一直往上找,最终找到根

Find(A):
def find(A):
    if parent[A] != A:
        return find(parent[A])  # 递归找根
    return A

Union(A, B):
def union(A, B):
    rootA = find(A)
    rootB = find(B)
    if rootA != rootB:
        parent[rootB] = rootA  # 把 B 的根接到 A 的根上

2.2 路径压缩(核心优化)

面试官追问:"什么是路径压缩?为什么需要?"

问题:链式结构

初始化:
parent = [0, 1, 2, 3, 4]
每个节点的父节点是自己

Union(0, 1):parent[1] = 0
Union(1, 2):parent[2] = 1(链式)
Union(2, 3):parent[3] = 2
...

最终:
0 ← 1 ← 2 ← 3 ← 4
|   |   |   |   |
0   0   0   0   0

找 4 的根:
find(4) → find(3) → find(2) → find(1) → find(0) = 0
要找 4 次!

路径压缩优化:

在找根的过程中,把所有经过的节点都直接指向根:

find(4):
4 → 3 → 2 → 1 → 0(找到根)
压缩后:
0 ← 0
↑   ↑
4 → 3 → 2 → 1(都直接指向 0)

下次再找 4 的根,只需要一步!

复杂度:接近 O(1)!

2.3 按秩合并

按秩合并:把小的树接到大的树上

为什么不随意合并?
因为合并后树可能会变深,
下次 Find 就要走更多步。

按秩合并策略:
- rank[i] = i 所在树的高度
- 合并时,把矮的树接到高的树上

为什么有效?
因为树的高度不会增加超过 log n!
结合路径压缩,几乎是 O(1)。

三、面试真题讲解

3.1 朋友圈数量(LeetCode 547)

考察点:并查集的典型应用

问题:同学之间认识,问有多少个朋友圈

示例:
M = [[1,1,0],
     [1,1,0],
     [0,0,1]]

M[i][j] = 1 表示 i 和 j 是朋友
朋友关系是传递的!

思路:
- 每个人是一个集合
- 遍历矩阵,如果 M[i][j] = 1,Union(i, j)
- 最后数有多少个独立的集合

这就是并查集的直接应用!

3.2 岛屿数量(变种)

考察点:二维网格的连通性

问题:二维网格中,'1' 是陆地,'1' 相连形成一个岛屿

思路:
- 每个陆地格子是一个节点
- 相邻的陆地(在上下左右)相连
- 用并查集统计连通分量

但通常 BFS/DFS 更直观:
- 遍历网格
- 如果是 '1' 且未访问
- BFS/DFS 标记所有相连的 '1'
- 岛屿数 + 1

四、面试总结

并查集核心要点

┌─────────────────────────────────────────────────┐
│                  并查集核心                       │
├─────────────────────────────────────────────────┤
│                                                  │
│  解决的问题:连通性                               │
│                                                  │
│  核心操作:                                      │
│  - Find:找根节点                               │
│  - Union:合并集合                              │
│                                                  │
│  优化:                                          │
│  - 路径压缩:让查找几乎 O(1)                   │
│  - 按秩合并:避免树变深                         │
│                                                  │
│  时间复杂度:                                    │
│  - 近乎 O(1),准确说是 α(n)(Ackermann 反函数)│
│                                                  │
└─────────────────────────────────────────────────┘

面试能加分的回答

1. 能解释路径压缩
   "在找根的过程中,把所有节点直接指向根,
    下次找就快了"

2. 能解释按秩合并
   "把矮的树接到高的树上,
    保证树的高度不会太大"

3. 能说出应用场景
   "连通性问题、朋友圈、岛屿数量、检测环"

相关题目

相关标签