算法目标
Union-Find 算法主要需要实现 API:
class UF
{
/* 将 p 和 q 连接 */
public void union(int p, int q);
/* 判断 p 和 q 是否连通 */
public boolean connected(int p, int q);
/* 返回图中有多少个连通分量 */
public int count();
}
判断连通性非常实用,比如说编译器判断同一个变量的不同引用,比如社交网络中的朋友圈计算等等
算法实现
使用森林(若干棵树)来表示图的动态连通性,用数组来具体实现这个森林
Union合并两棵树
public void union(int p, int q)
{
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ)
return;
// 将两棵树合并为一棵
parent[rootP] = rootQ;
// parent[rootQ] = rootP 也一样
count--; // 两个分量合二为一
}
find操作返回根节点
private int find(int x)
{
// 根节点的 parent[x] == x
while (parent[x] != x)
x = parent[x];
return x;
}
连通性判断
public boolean connected(int p, int q)
{
int rootP = find(p);
int rootQ = find(q);
return rootP == rootQ;
}
返回连通分量个数
public int count()
{
return count;
}
算法优化
平衡性优化
新增size[]数组记录树的节点数
在union操作时,将较小的树接在大的树下面
路径压缩
我们能不能进一步压缩每棵树的高度,使树高始终保持为常数,这样find
就能以 O(1) 的时间找到某一节点的根节点,相应的,connected
和union
复杂度都下降为 O(1)
private int find(int x)
{
while (parent[x] != x)
{
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
用find
函数每次向树根遍历的同时,顺手将树高缩短了,最终所有树高都不会超过 3