一、并查集概述
1.1 支持操作
- Find:查找元素所属集合(根)
- Union:合并两个集合
1.2 应用场景
- 连通分量
- 最小生成树(Kruskal)
- 等价关系
- 离线查询
二、基本实现
2.1 数组版
class UnionFind { int[] parent; int[] rank; int count; UnionFind(int n) { parent = new int[n]; rank = new int[n]; count = 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]; } boolean union(int x, int y) { int px = find(x), py = find(y); if (px == py) return false; if (rank[px] < rank[py]) { parent[px] = py; } else if (rank[px] > rank[py]) { parent[py] = px; } else { parent[py] = px; rank[px]++; } count--; return true; } boolean connected(int x, int y) { return find(x) == find(y); } }
|
三、复杂度分析
3.1 反阿克曼函数
经过路径压缩和按秩合并优化后,单次操作均摊复杂度为 ( O(\alpha(n)) ),其中 ( \alpha ) 是反阿克曼函数,增长极慢(实际中可视为常数)。
四、经典应用
4.1 省份数量
public int findCircleNum(int[][] isConnected) { int n = isConnected.length; UnionFind uf = new UnionFind(n); for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (isConnected[i][j] == 1) uf.union(i, j); } } return uf.count; }
|
4.2 冗余连接
public int[] findRedundantConnection(int[][] edges) { UnionFind uf = new UnionFind(edges.length + 1); for (int[] edge : edges) { if (!uf.union(edge[0], edge[1])) return edge; } return new int[0]; }
|
五、总结
并查集是处理动态连通性问题的高效数据结构。通过路径压缩和按秩合并,几乎达到常数时间复杂度。其核心思想是”代表元”:每个集合选一个根节点作为代表。