Union-Find并查集算法详解

算法目标

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) 的时间找到某一节点的根节点,相应的,connectedunion复杂度都下降为 O(1)

private int find(int x)
{
    while (parent[x] != x) 
    {
        parent[x] = parent[parent[x]];
        x = parent[x];
    }
    return x;
}

find函数每次向树根遍历的同时,顺手将树高缩短了,最终所有树高都不会超过 3

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值