并查集相关知识

并查集 :是一种树形的数据结构,用于处理一些不相交的集合的合并和查询问题。通常在使用中以森林来表示。

作用:可以判断网络(不单是计算机网络)中节点的连接状态。

对一组数据主要支持以下几个动作:

union(p,q)//将p,q所在的集合合并为同一集合

isConnected(p,q)//查看p,q是否是同一集合的元素

find( p )//查找元素所在的集合,即根节点

代码实现:

``

//并查集接口
public interface UnionFind {
    int getSize();
    boolean isConnected(int p, int q);
    void unionElements(int p, int q);
}

``

/**
 * @author shy_black
 * @date 2019/4/22 15:57
 * @Description:
 * 下标 0 1 2 3 4 5 6 7 8 9
 * id:  0 1 2 3 4 5 6 7 8 9  元素
 *      0 1 0 1 0 1 0 1 0 1  id集合
 * quick find
 */
public class UnionFind_1 implements UnionFind {

    private int[] id;

    public UnionFind_1(int size) {
        id = new int[size];
        for(int i = 0;i < id.length; i++) {
            //每一个元素都属于自己的集合编号
            id[i] = i;
        }
    }

    /**
     *
     * @param p:查找元素p所对应的集合编号
     * @return
     *
     */
    private int find(int p) {
        if(p < 0 && p >= id.length)
            throw new IllegalArgumentException("p is out of bound...");
        return id[p];
    }
    @Override
    public int getSize() {
        return id.length;
    }

    /**
     * @param p
     * @param q
     * @return
     * 查看元素p和元素q是否所属一个集合
     */
    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }
    //合并元素p和元素q所属的集合
    //将pID修改为qID
    @Override
    public void unionElements(int p, int q) {
        int pID = find(p);
        int qID = find(q);

        if(pID == qID)
            return;
        for(int i = 0;i < id.length;i++) {
            if(id[i] == pID)
                id[i] = qID;
        }
    }
}

优化:当前的unionElement(p,q);方法在每次的合并集合时,会将所有的p集合中的元素修改为q集合的元素。如果p集合很大,但q集合很小时,这样做很浪费时间。

``

//以下的方法增加了一个记录集合大小的数组 sz,
//将数组换成了树结构,在find时可以直接找到集合的根节点。
//在unionElement时,直接根据节点的大小将根节点合并,
//而不是遍历数组进行更改,效率提高
//
public class UnionFind_2 implements UnionFind {
    private int[] parent;
    private int[] sz;

    public UnionFind_2(int size) {
        parent = new int[size];
        sz = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i;
            sz[i] = 1;
        }
    }


    @Override
    public int getSize() {
        return parent.length;
    }

    //查找过程,查找元素p对应的集合编号
    //O(h)复杂度,h为树的高度
    private int find(int p) {
        if (p < 0 && p >= parent.length) {
            throw new IllegalArgumentException("非法下标");
        }
        while (p != parent[p]) {
            //如果p != parent[p],让p等于parent[p],不断寻找p的根
            p = parent[p];
        }
        return p;
    }

    //查看p和q是否所属一个集合
    //O(h)复杂度,h为树的高度
    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    @Override
    public void unionElements(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);

        if (pRoot == qRoot)
            return;
        //如果p的节点数小于q的节点数,
        // 让p的根节点指向q的根节点
        if (sz[pRoot] < sz[qRoot]) {

            parent[pRoot] = qRoot;
            sz[qRoot] += sz[pRoot];
        }else {
            parent[qRoot] = pRoot;
            sz[pRoot] += sz[qRoot];

        }
    }
}

优化:还可以将记录一个rank[]数组,记录集合的层数,根据层数将层数小的根节点合并到层数多的根节点上。如果层数相同,合并节点后rank[Root]加一即可。

``

@Override
public void unionElements(int p, int q) {
    int pRoot = find(p);
    int qRoot = find(q);

    if(rank[pRoot] < rank[qRoot])
        parent[pRoot] = qRoot;
    else if(rank[pRoot] > rank[qRoot])
        parent[qRoot] = pRoot;
    else {
        parent[pRoot] = qRoot;
        rank[qRoot]  += 1;
    }

}

最后,可以通过在find方法中修改方法体内容达到路径压缩的作用

``

//在当前节点不是根节点时,通过将当前节点连接到当前节点的父亲节点的父亲节点上,达到路径压缩的目的,从而减少并查集的深度,增加查找效率
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值