并查集是一种树形的数据结构,用于处理不交集的合并(Union)及查询(Find)问题
Find:确定元素属于哪一个子集,它可以被用来确定两个元素是否属于同一子集
Union:将两个子集合并成同一个集合
模板
1.with path compression
class DSU {
int[] parent;
public DSU(int N){
parent = new int[N];
for(int i = 0;i < N;i++) parent[i] = i;
}
public int find(int x){
if(parent[x] != x) parent[x] = find(parent[x]);
}
public void union(int x, int y){
parent[find(x)] = find(y);
}
}
2.improved with ranked
class DSU {
int[] parent;
int[] rank;
public DSU (int N) {
parent = new int[N];
rank = new int[N];
for (int i = 0;i < N;i++) parent[i] = i;
Arrays.fill(rank,1);
}
public int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]);
return parent[x];
}
public void union(int x,int y) {
int rootX = find(x),rootY = find(y);
if (rootX == rootY) return;
if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else { //只有rank相等的情况才需要维护rank
parent[rootX] = rootY;
rank[rootY]++;
}
}
}
3.improved with size(weighted)
class DSU {
int[] parent;
int[] size;
public DSU (int N) {
parent = new int[N]; size = new int[N];
for (int i = 0;i < N;i++) parent[i] = i;
Arrays.fill(size,1);
}
public int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]);
return parent[x];
}
public void union(int x,int y) {
int rootX = find(x),rootY = find(y);
if (rootX == rootY) return;
if (size[rootX] <= size[rootY]) {
parent[rootX] = rootY;
size[rootY] += size[rootX];
} else if (size[rootX] > size[rootY]) {
parent[rootY] = rootX;
size[rootY] += size[rootX];
}
}
}
时间复杂度
在我们使用Quick Union版本的并查集使用树形结构来组织节点的关系
那么性能跟树的深度有关系,简称O(n),以前介绍二分搜索树的时候,时间复杂度也是O(n)
但是并查集并不是一个二叉树,而是一个多叉树,所以并查集的查询和合并时间复杂度并不是O(log n)
在加上rank和路径压缩优化后,并查集的时间复杂度为O(log n)
总结
1.不可以update rank在path compression之后,因为可能存在着其他node有更长的path通向那个root
2.不需要更新rank在path compression之后,因为rank提供的只是一个upper bound,而不是exact value,一个较小的数值也可以符合upper bound,upper bound只是为了避免worst case
因为二者目的不一样,一个是为了避免worst case,一个是为了amortize