概述
并查集是用来解决集合的合并和查找的数据结构。提供两个基本操作即
- find(int i):查找I所归属的集合,通常使用find(i)和find(j)判断i和j是否连通,即是否属于同一个集合。
- union(int i , int j):合并两个集合,操作后I所在集合和所有元素和J所在集合的所有元素都连通。
每个方法的对应思路:
- 初始化:用森林表示图的连通性,每个节点都有一个指针指向父节点。一开始各个节点都是单独的,所以指向自己。
- 合并:让节点较少的树连接到较多的树下面,避免出现链式情况。连通分量–。
- 判断连通:若两个节点连通,则一定有相同的根节点。
- 查找根节点:找到给定节点的根节点,用路径压缩在查找时顺手压缩树的高度为常数。
关键点:
- parent[] 存每个节点的父节点;
- size[] 记录每棵树的重量,让小重量的树连接在大重量树的下面;
- find() 进行路径压缩,保证树高度为常数。
模板
public class UnionFind {
//连通分量个数
private int count;
//存储一棵树
private int[] parent;
//记录树的高度/重量
private int[] size;
public UnionFind(int n) {
this.count = n;
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
size[i] = 1;
}
}
/**
* 合并两个集合
* @param p
* @param q
*/
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ) {
return;
}
//调整平衡,小树连在大树之后
if (size[rootP] > size[rootQ]) {
parent[rootQ] = rootP;
size[rootP] += size[rootQ];
} else {
parent[rootP] = rootQ;
size[rootQ] += size[rootP];
}
count--;
}
public boolean connected(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
return rootP == rootQ;
}
/**
* 找到元素归属的集合
* @param x
* @return
*/
private int find(int x) {
//路径压缩
//每次把自己变成父结点的兄弟节点
while (parent[x] != x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
public int count() {
return count;
}
}
例题
深度搜索DFS的问题大多都能用并查集解决。