并查集的五种实现方法

并查集的五种实现方式

并查集可确定两个结点是否连接,合并两个结点,不考虑元素的内容用数组来保存。

接口实现:

public interface UF {
    /*
     * 并查集的接口
     * getsize 元素的个数
     * isConnected是否连接
     * union合并
     * */
    /*获得元素个数*/
    int getSize();

    /*p q 表示元素在数组中的索引
     * 判断p q 是否连接
     * */
    boolean isConnected(int p, int q);

    /*将p,q连接起来*/
    void union(int p, int q);
}

方法一:

public class UnionFindFirst implements UF{
    public int[] id;
    public int size;

    /*初始化
    * 为每一个元素(数组索引)创建一个集合(数组对应索引的值)
    * 属于集合表示
    * 1 2 3 4 5 6 7 8
    * 0 0 0 0 1 1 1 1
    * */
    public UnionFindFirst(int size) {
        this.size = size;
        id=new int[size];
        for (int i = 0; i < size; i++) {
            id[i]=i;
        }
    }
    public int find(int index){
        return id[index];
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p)==find(q);
    }

    @Override
    public void union(int p,int q) {
        int pNum=find(p);
        int qNum=find(q);
        if(pNum==qNum){
            return;
        }
        /*
        * 遍历;把qNum集合的值,全部变为pNum集合的值
        * 即把两个集合,融合为两个
        * */
        for (int i = 0; i < getSize(); i++) {
            if(id[i]==qNum){
                id[i]=pNum;
            }
        }
    }
}

方法二;

public class UnionFindSecond implements UF{
/*
* 将每个元素看做结点,节点之间相连接,形成树的结构
*eg:将结点1和结点3合并,将3和1的根节点相连接
* 1-->2 2-->2 3-->2
* */
    private int size;
    private int[] parents;

    public UnionFindSecond(int size) {
        this.size = size;
        parents=new int[size];
        for (int i = 0; i < size; i++) {
            parents[i]=i;
        }
    }

    //查找索引为index的结点所在的根节点
    public int find(int index){
        while(index!=parents[index]){
            index=parents[index];
        }
        return index;
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p)==find(q);
    }

    /*将p所在的根节点指向q所在的根节点*/
    @Override
    public void union(int p, int q) {
        int pRoot=find(p);
        int qRoot=find(q);
        if(pRoot==qRoot){
            return;
        }
        parents[pRoot]=qRoot;
    }
}

方法三:

public class UnionFindThird implements UF {
    /*基于size进行优化,将结点少的挂在结点多的上边;
     *只修改union方法
     */

    int size;
    int[] parents;
    int[] nums;

    public UnionFindThird(int size) {
        this.size = size;
        parents = new int[size];
        nums = new int[size];
        for (int i = 0; i < size; i++) {
            parents[i] = i;
            nums[i] = 1;
        }
    }

    //查找索引为index的结点所在的根节点
    public int find(int index) {
        while (index != parents[index]) {
            index = parents[index];
        }
        return index;
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    /*将p所在的根节点指向q所在的根节点*/
    @Override
    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        /*判断节点的个数*/
        if (pRoot != qRoot) {
            if (nums[pRoot] > nums[qRoot]) {
                parents[qRoot] = pRoot;
                nums[pRoot] += nums[qRoot];

            } else {
                parents[pRoot] = qRoot;
                nums[qRoot] += nums[pRoot];
            }
        }
    }
}

方法四:

public class UnionFindFourth implements UF {
    /*基于heigth进行优化,将高度低的树合并在高度高的树上;
     *只修改union方法
     */

    int size;
    int[] parents;
    int[] height;

    public UnionFindFourth(int size) {
        this.size = size;
        parents = new int[size];
        height = new int[size];
        for (int i = 0; i < size; i++) {
            parents[i] = i;
            height[i] = 1;
        }
    }

    //查找索引为index的结点所在的根节点
    public int find(int index) {
        while (index != parents[index]) {
            index = parents[index];
        }
        return index;
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    /*将p所在的根节点指向q所在的根节点*/
    @Override
    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        /*判断节点的个数*/
        if (pRoot != qRoot) {
            if (height[pRoot] > height[qRoot]) {
                parents[qRoot] = pRoot;
            } else if(height[pRoot]<height[qRoot]){
                parents[pRoot] = qRoot;
            }else{
                parents[pRoot]=qRoot;
                height[qRoot]+=1;
            }
        }
    }
}

方法五:

public class UnionFindFifth implements UF {
    /*
     * 路径压缩
     * 方法:
     * parents[p]=parents[parents[p]];
     * 只需要改变find方法
     *
     * */
    int size;
    int[] parents;

    public UnionFindFifth(int size) {
        this.size = size;
        parents = new int[size];
        for (int i = 0; i < size; i++) {
            parents[i] = i;
        }
    }

    //查找索引为index的结点所在的根节点
    public int find(int index) {
        while (index != parents[index]) {
            parents[index]=parents[parents[index]];
            index = parents[index];
        }
        return index;
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    /*将p所在的根节点指向q所在的根节点*/
    @Override
    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        /*判断节点的个数*/
        if (pRoot != qRoot) {
            parents[qRoot] = pRoot;
        }
    }
}

测试:

import java.util.Random;

public class UFTest {
    /**
     * @param uf 测试并查集
     * @param count 测试合并和是否连接的次数
     */
    public static void test(UF uf, int count) {
        long startTime = System.nanoTime();
        Random random = new Random();
        // 合并操作
        for (int i = 0; i < count; i++) {
            uf.union(random.nextInt(uf.getSize()), random.nextInt(uf.getSize()));
        }

        // 判断是否连接
        for (int i = 0; i < count; i++) {
            uf.isConnected(random.nextInt(uf.getSize()), random.nextInt(uf.getSize()));
        }
        long endTime = System.nanoTime();
        System.out.println("共用了" + (endTime - startTime) / 1000000000.0 + "s");
    }

    public static void main(String[] args) {
        int size = 10000;
        int count = 100000;
        UF uf1 = new UnionFindFirst(size);
        System.out.println("方法一:");
        test(uf1, count);
        UF uf2 = new UnionFindSecond(size);
        System.out.println("方法二:");
        test(uf2, count);
        UF uf3 = new UnionFindThird(size);
        System.out.println("方法二基于size的优化:");
        test(uf3, count);
        UF uf4 = new UnionFindFourth(size);
        System.out.println("方法二基于height的优化:");
        test(uf4, count);
        UF uf5 = new UnionFindFifth(size);
        System.out.println("方法二基于压缩路径的优化:");
        test(uf5, count);


    }
}

测试结果:

方法一:
共用了0.0900325s
方法二:
共用了1.0276614s
方法三基于size的优化:
共用了0.0335062s
方法四基于height的优化:
共用了0.0159129s
方法五基于压缩路径的优化:
共用了0.0136944s
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集是一种常用的数据结构,用于管理一个不相交集合的数据。在并查集中,每个元素都有一个父节点指向它所属的集合的代表元素。通过查找和合并操作,可以判断两个元素是否属于同一个集合,并将它们合并到同一个集合中。 在解决某些问题时,可以使用并查集进行优化。例如,在区间查询中,可以通过优化并查集的查询过程,快速找到第一个符合条件的点。 对于拆边(destroy)操作,一般的并查集无法直接实现这个功能。但是可以通过一个巧妙的方法来解决。首先,记录下所有不会被拆除的边,然后按照逆序处理这些指令。遇到拆边操作时,将该边重新加入并查集中即可。 在实现并查集时,虽然它是一种树形结构,但只需要使用数组就可以实现。可以通过将每个元素的父节点记录在数组中,来表示元素之间的关系。通过路径压缩和按秩合并等优化策略,可以提高并查集的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [「kuangbin带你飞」专题五并查集专题题解](https://blog.csdn.net/weixin_51216553/article/details/121643742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [并查集(详细解释+完整C语言代码)](https://blog.csdn.net/weixin_54186646/article/details/124477838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值