并查集的三种优化【Java版】

本文详细介绍了并查集的三种优化方法:快速union、基于size的优化和基于rank的优化。快速union通过合并根节点实现高效操作;基于size的优化通过比较根节点的元素数目,避免形成线性链表;基于rank的优化通过比较树的深度,确保合并后树的高度得到控制,从而保持find操作的高效性。

/**
*优化方法二:快速union,慢find
*方法一合并时,需要将p元素集合中所有元素都合并到q集合中,效率为O(n)
*现只需将p元素所属集合中根节点所在的元素和q合并即可,构成一棵树,O(1)
*每个元素均有自己的父节点,根节点才是最终元素所属的集合
*合并时,将两个元素的根节点合并
*/

public class UnionFind2 {
    private int count;
    private int[]parent;
    public UnionFind2(int n) {
        count=n;
        parent=new int[n];
    //初始化时,数组中的元素均指向自己,以自己为父节点
        for(int i=0;i<count;i++) {
            parent[i]=i;
        }
    }
    //不断寻找根节点即元素p所属集合
    int find(int p) {
        //p不能越界
        if(p<0||p>=count)
            return -1;
 //如果元素p不是自己的本身的父节点,则p存在根节点,根节点即为p所属的最终集合
        while(p!=parent[p]) {
            p=parent[p];
        }
        return p;
    }
    boolean isConnected(int p,int q) {
        return find(p)==find(q);
    }
    //将两个元素并在一个集合
    public void union(int p,int q) {
        int pRoot=find(p);
        int qRoot=find(q);
        //若根节点相同即在一个集合里
        if(pRoot==qRoot)
            return;
        //若不在一个集合里,即将两个根节点相连
        //将p的根节点指向q的根节点;
        parent[pRoot]=qRoot;        
    }   
    //测试数组
    public void printParent() {
        for(int a:parent)
            System.out.print(a+" ");
        System.out.println();
    }

}

/**
*优化方法三【基于size的优化】
*优化:增加一个存储数组下标元素每个根节点的元素数目
*因为方二中,总是固定将p的根节点指向q,有可能导致成为线性链表,树的深度变大
*所以可以提前判断下根节点的数目,总是让数目小的指向数目大的,让树尽量矮
*/

public class UnionFind3 {
    private int count;
    private int[]parent;
    //size[i]即表示以i为根的集合中元素的个数
    private int[]size;
    public UnionFind3(int n) {
        count=n;
        parent=new int[n];
        size=new int[n];
        for(int i=0;i<count;i++) {
            parent[i]=i;
            //初始时,每个集合中元素个数均为1
            size[i]=1;
        }
    }

    int find(int p) {
        if(p<0||p>=count)
            return -1;
        while(p!=parent[p])
            p=parent[p];
        return p;
    }
    boolean isConnected(int p,int q) {
        return find(p)==find(q);
    }
    public void union(int p,int q) {
        int pRoot=find(p);
        int qRoot=find(q);
        if(pRoot==qRoot)
            return;
        if(size[pRoot]<size[qRoot]) {
            parent[pRoot]=qRoot;
            //要维护树的个数
            size[qRoot]+=size[pRoot];
        }else {
            parent[qRoot]=pRoot;
            size[pRoot]+=size[qRoot];
        }
    }
    public void printParent() {
        for(int a: parent)
            System.out.print(a+" ");
        System.out.println();
    }
}

/**
*优化方法四【基于rank的优化】
*方法三中只考虑集合元素个数,仍然会存在树的深度问题,
*EG:一个树深度低但是个数多,另一棵树深高但个数少,
*由个数判断,最后会让树深指向树浅,导致深度不断增加
*并查集越深,就越接近线性,find函数就越接近O(n)
*优化为:合并时,谁的深度深,谁就是新的根。
*这样集合的深度最多是最大深度的集合的深度,而不会让深度增加
*/

public class UnionFind4 {
    private int count;
    private int[] parent;
    //rank[i]即表示以i为根的集合中树的深度
    private int[]rank;
    public UnionFind4(int n) {
        count=n;
        parent=new int[n];
        rank=new int[n];
        for(int i=0;i<count;i++) {
            parent[i]=i;
            //初始化时,每个元素树的深度均为1
            rank[i]=1;
        }
    }
    int find(int p) {
        while(p!=parent[p])
            p=parent[p];
        return p;
    }
    boolean isConnected(int p,int q) {
        return find(p)==find(q);
    }
    public void union(int p,int q) {
        int pRoot=find(p);
        int qRoot=find(q);
        if(pRoot==qRoot)
            return;
        if(rank[pRoot]<rank[qRoot]) {
            parent[pRoot]=qRoot;
        }else if(rank[pRoot]>rank[qRoot]) {
            parent[qRoot]=pRoot;
        }else {
            //深度相等时随便合并不影响结果
            parent[pRoot]=qRoot;
            rank[qRoot]+=1;
        }
    }
    //测试数组
        public void printParent() {
            for(int a:parent)
                System.out.print(a+" ");
            System.out.println();
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值