← 返回题目列表

什么是并查集(Union-Find)?路径压缩和按秩合并是怎么优化的?

高频 中等 第 7 / 30 题 更新于 2026/07/28
并查集Union-Find连通性

简化版

并查集是专门处理「动态连通性」的数据结构,支持两个操作:find(x) 找 x 所在集合的代表(根)、union(a,b) 合并两个集合。用「每个元素指向父节点、同一集合共用一个根」的森林实现。两个优化让它接近 O(1):路径压缩(find 时把路径上的节点直接挂到根下)、按秩/按大小合并(把矮树挂到高树下,避免树变高)。用于判断连通、环检测、Kruskal、朋友圈。

详细版

class UnionFind {
    int[] parent, rank;
    UnionFind(int n) {
        parent = new int[n]; rank = new int[n];
        for (int i = 0; i < n; i++) parent[i] = i;  // 初始各自为根
    }
    int find(int x) {                    // 路径压缩
        if (parent[x] != x) parent[x] = find(parent[x]); // 直接挂到根
        return parent[x];
    }
    void union(int a, int b) {           // 按秩合并
        int ra = find(a), rb = find(b);
        if (ra == rb) return;            // 已在同一集合
        if (rank[ra] < rank[rb]) { int t = ra; ra = rb; rb = t; } // 保证 ra 更高
        parent[rb] = ra;                 // 矮的挂到高的下
        if (rank[ra] == rank[rb]) rank[ra]++;
    }
    boolean connected(int a, int b) { return find(a) == find(b); }
}
  • find:顺着 parent 往上找到根;路径压缩让沿途节点直接指向根。
  • union:找到两个根,把一个挂到另一个下面(按秩:矮的挂高的)。
  • connected:两者根相同即连通。

完整版教学

一、并查集解决什么问题

并查集专治「动态连通性」:不断地「把 a 和 b 归为一组」,并随时查询「a 和 b 是不是同一组」。比如社交网络里不断加好友、判断两人是否在同一朋友圈;网络节点不断连线、判断是否连通。这类「合并 + 查询归属」的问题,并查集用近乎常数的时间搞定,比每次 DFS 判连通高效得多。

二、核心思想:用森林表示集合

并查集把每个集合表示成一棵树,树根作为这个集合的代表parent[x] 指向 x 的父节点,根节点的 parent 是自己。

  • 判断两个元素是否同组:各自 find 到根,根相同就是一组。
  • 合并两个集合:把一个集合的根挂到另一个集合的根下面。

初始时每个元素自成一棵单节点树(自己是自己的根)。

三、优化一:路径压缩

朴素 find 要顺着 parent 一路爬到根,如果树很高就慢。路径压缩:在 find 的过程中,把沿途经过的所有节点直接指向根。这样下次再 find 这些节点就一步到位。经过压缩,树会变得非常扁平(几乎所有节点直接挂在根下)。

四、优化二:按秩 / 按大小合并

如果合并时随意挂,可能把高树挂到矮树下,让树越来越高。按秩合并:总是把较矮的树挂到较高的树下面(rank 近似树高),这样合并后的树高不会轻易增加。按大小合并类似,把节点少的挂到节点多的下面。两者都是为了控制树高

五、复杂度:接近 O(1)

单独用路径压缩、或单独用按秩合并,单次操作是 O(log n)。两个优化同时用,单次操作的均摊复杂度是 O(α(n)),其中 α 是反阿克曼函数——它增长极其缓慢,对任何现实规模的 n(哪怕宇宙原子数),α(n) 都 ≤ 4。所以并查集操作实际上就是常数时间。这是它高效的根本。

六、常见误区与追问

考点正确口径
find找到元素所在集合代表元
union合并两个集合
路径压缩find 时把路径节点直接挂到根
按秩合并让小树挂到大树下
find(x):
  if parent[x] != x:
    parent[x] = find(parent[x])
  return parent[x]

并查集擅长动态连通性:只关心“是否在同一集合”,不关心具体路径长什么样。

  • 误区:并查集能回答两点之间的具体路径。 它只维护连通分量代表,不能恢复路径;要路径需 DFS/BFS 或额外记录。
  • 误区:union 直接随便挂不会影响复杂度。 长期随便挂可能形成高链,按秩或按大小合并能控制树高。
  • 误区:路径压缩只优化当前节点。 递归回溯会把整条查找路径上的节点都直接连到根,后续查询更快。
  • 追问:并查集适合哪些题? 连通性、朋友圈、省份数量、无向图判环、Kruskal 最小生成树。
  • 追问:复杂度为什么接近 O(1)? 路径压缩加按秩合并后,均摊复杂度是反阿克曼函数级别,实际可视作常数。
  • 追问:如何统计集合数量? 初始化为 n,每次成功 union 两个不同集合时数量减一。

七、加强记忆

并查集(Union-Find)处理动态连通性find 找集合代表(根)、union 合并、connected 判同组。用森林表示集合(parent 指向父、根代表集合)。两个优化:路径压缩(find 时把沿途节点直接挂到根、树变扁平)+ 按秩/按大小合并(矮树挂高树下、控制树高)。两者同用,单次操作近 O(α(n))≈O(1)(α 是反阿克曼函数,恒 ≤ 4)。应用:连通判断、环检测、Kruskal、朋友圈。