union-find算法实现
union-find问题就是动态图连通性问题,即图中两点是否连通。
加权quick-union算法
加权quick-union算法是解决union-find问题比较高效的办法,主要思想为:
同一连接分量的节点使用树进行连接,find()函数可以查找节点所在连接分量树的根节点,通过比较根节点是否相同来判断节点是否属于同一连通分量。
连接时如果两节点不在同一连通分量上则需要将两个连通分量并到同一棵树上,此处便是加权的思想:将节点数较小的树的根节点连接到节点数较大的根节点上,这样可使树的深度尽量降低。
具体算法如下:
import java.util.Random;
/**
* 加权quick-union算法
* union函数连接节点
* connected函数判断节点是否连通
* <p>
* 对于n个节点,m条连接最多访问数组cMlgN次
*
* @author zhkp
*/
public class WeightedQuickUnionUF {
private int[] id; // 链接数组
private int[] sz; // 各个节点所对应树的大小
private int count; // 连通分量的数量
public WeightedQuickUnionUF(int N) {
count = N;
id = new int[N];
sz = new int[N];
for (int i = 0; i < N; i++) {
id[i] = i;
sz[i] = 1;
}
}
/**
* @return 连通分量数量
*/
public int count() {
return count;
}
/**
* @param p 节点p
* @param q 节点q
* @return 节点p、q是否连通
*/
public boolean connected(int p, int q) {
return find(p) == find(q);
}
/**
* @param p 查询节点
* @return 查询节点对应根节点
*/
public int find(int p) {
while (p != id[p]) {
p = id[p];
}
return p;
}
public void union(int p, int q) {
int i = find(p);
int j = find(q);
// 若不连通则将数量较小的树连接到数量较大的树上
if (i == j) {
return;
} else if (sz[i] > sz[j]) {
id[j] = i;
sz[i] += sz[j];
} else {
id[i] = j;
sz[j] += sz[i];
}
count--;
}
/**
* 测试样例
*/
public static void main(String[] args) {
int nodeNum = 10;
int edgeNum = 10;
Random random = new Random();
WeightedQuickUnionUF w = new WeightedQuickUnionUF(nodeNum);
int a, b;
for (int i = 0; i < edgeNum; i++) {
a = random.nextInt(nodeNum);
b = random.nextInt(nodeNum);
w.union(a, b);
System.out.println(a + " - " + b);
}
a = random.nextInt(nodeNum);
b = random.nextInt(nodeNum);
System.out.println(a + "与" + b + (w.connected(a, b) ? "已" : "不") + "连通");
System.out.println("连通分量数量:" + w.count());
}
}