有两个优化点:
- rank排序 记录两个合并的根的rank rank低的合并到rank高的上 这个优化目的是避免树的深度增加
- 路径压缩 parent[p] = parent[parent[p]] 这行代码将该节点指向它父亲节点的父亲节点 可以降低树的深度。
public class UnionFind4 {
private int[] parent; // parent[i]记录i的父亲节点
private int[] rank; // rank[i]表示以i为根的集合的深度
public UnionFind4(int capacity) {
parent = new int[capacity];
rank = new int[capacity];
// 初始化 每个节点的父亲节点都是自己 且深度均为1(自己)
for (int i = 0; i < parent.length; i++) {
parent[i] = i;
rank[i] = 1;
}
}
// 查找某一节点的根节点
// O(h)复杂度, h为树的高度
private int find(int p) {
if (p < 0 || p > parent.length) {
throw new IllegalArgumentException("p is out of bound");
}
while (p != parent[p]) {
parent[p] = parent[parent[p]];
p = parent[p];
}
return p;
}
// 判断两节点是否连通(是否拥有同一根节点)
// O(h)复杂度, h为树的高度
public boolean isConnected(int p, int q) {
return find(p) == find(q);
}
// 将两个节点合并
// O(h)复杂度, h为树的高度
public void unionElements(int p, int q) {
// 查找各自的根节点
int pRoot = find(p);
int qRoot = find(q);
// 如果他们的根节点相同 则已经连接 无需操作
if (pRoot == qRoot)
return;
// 将rank低的节点指向rank高的节点
if (rank[pRoot] < rank[qRoot]) {
parent[pRoot] = qRoot;
} else if (rank[qRoot] < rank[pRoot]){
// 反之则把p的根节点指向q
parent[qRoot] = pRoot;
} else {
// 此时两个根节点的深度相同 谁指向谁都可以
parent[qRoot] = pRoot;
// 此时需要维护rank 被指向的节点rank需要+1
rank[pRoot] += 1;
}
}
}