并查集:quick-find、quick-union和加权quick-union

并查集

理论基础

等价关系:自反性、对称性和传递性

等价关系能够将对象分为多个等价类。

应用:动态连通性问题

术语:

  • 对象:触点
  • 整数对:连接
  • 等价类:连通分量或分量

成本模型:访问任意数组元素的次数,无论读写

union-find

以触点为索引的 id[] 数组来确定两个触点是否存在于相同的连通分量中

public class UF{
	int[] id;	// 分量id(以触点作为索引)
    int count;	// 分量数量,初始化为 N
    
    // 初始化分量 id 数组
    public UF(int N){	
        count=N;
        id = new int[N];
        // 每个分量只含触点本身
        for(int i=0;i<N;i++){
            id[i] = i;
        }
    }
    // 如果p和q存在于同一个分量中则返回true
    boolean connected(int p, int q){
        return find(p) == find(q);
    }
    // p所在的分量的标识符
    int find(int p){}
    // 如果两个触点在不同的分量中,则会将两个分量归并且分量总数减一
    void union(int p, int q){}
}
quick-find 算法

保证当且仅当 id[p] == id[q] 时 p 和 q 是连通的,即在同一个连通分量中的所有触点在 id[] 中的值必须全部相同。

  1. 检查 p 和 q 是否已在同一个连通分量中

    1. 已在,则不需要采取任何行动

    2. 不在,p 所在的连通分量中的所有触点的 id[] 均为同一个值,q 同理为另一个值

      遍历整个数组,将所有和 id[p] 相等的元素值变为 id[q]

O(N2)

// p所在的分量的标识符; O(1)
int find(int p){
    return id[p];
}
// 将p和q归并到相同的分量中; O(N)
void union(int p, int q){
    int pID = find(p);
    int qID = find(q);
    
    // 如果p和q已经在相同的分量之中则不需要采取任何行动
    if(pID==qID){
        return;
    }
    // 将p的分量重命名为q的名称
    for(int i=0;i<id.length;i++){
        if(id[i]=pID){
            id[i] = qID;
        }
    }
    count---;	// 分量总数减一
}
quick-union算法

链接:每个触点所对应的 id[] 元素都是同一个分量中的另一个触点的名称(可能是它自己)。

  • 初始化时,每个节点的链接都指向自己
  • id[] 数组用父链接的形式表示了一片森林
  • 根节点的链接指向自己,表示一个分量。

当且仅当分别由两个触点开始的这个过程到达了同一个根触点时它们存在于同一个连通分量中。

当且仅当两个触点存在于相同的分量之中时它们对应的节点才会在同一棵树中。

O(N2)

// 找出分量的名称; O(h)
int find(int p){
    // 直到达到根触点
    while(p!=id[p]){
        // 从给定触点开始,由它的链接得到另一个触点
        p = id[p];
    }
    return p;
}
// 将 p 和 q 的根节点统一; O(h)
void union(int p, int q){
    int pRoot = find(p);	// 找到其根触点
    int qRoot = find(q);
    if(pRoot == qRoot){
        return;
    }
    id[pRoot] = qRoot;	// 将一个根触点链接到另一个
    count--;
}
加权quick-union算法

与其在 union() 中随意将一棵树连接到另一棵树,选择记录每一棵树的大小并总是将较小的树连接到较大的树上。

O(logN)

public class WeightedQuickUnionUF{
    int[] id;	// 父链接数组(由触点索引)
    int[] sz;	// (由触点索引的)各个根节点所对应的分量的大小
    int count;	// 连通分量的数量
    
    public WeightedQuickUnionUF(int N){
        count=N;
        for(int i=0;i<N;i++){
            id[i] = i;
        }
        for(int i=0;i<N;i++){
            sz[i] = ;
        }
    }
    
    // 跟随链接找到根节点; O(lgN)
    int find(int p){
        // 直到达到根触点
        while(p!=id[p]){
            // 从给定触点开始,由它的链接得到另一个触点
            p = id[p];
        }
        return p;
    }
    // O(lgN)
    void union(int p, int q){
        int i = find(p);
        int j = find(q);
        if(i==j){
            return;
        }
        // 将小树的根节点连接到大树的根节点
        if(sz[i]<sz[j]){
            id[i] = j;
            sz[j] += sz[i];
        }else{
            id[j] = i;
            sz[i] += sz[j];
        }
        count--;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值