并查集详解及实现

看完这篇文章你将会知道:

什么是并查集?

并查集的原理。

并查集的JAVA实现。

并查集这部分内容还是很简单的,相信只要认真学,你正在上小学二年级的表弟都能学得会。(´▽`ʃ♡ƪ)

目录

一、啥是并查集?

二、并查集的原理

三、并查集的JAVA实现


一、啥是并查集?

并查集是就是一个数据结构。🫡

好吧,说了等于没说。

重来,重来:

一个数据结构的出现,就对应了一个对数据操作的需求。

那么并查集要解决的需求是什么呢?


并查集主要对集合进行操作。

如图,假设这三个集合非常的大(树的深度与节点数很很很很很大):

需求1:目前有一个X这个节点,我要知道,X这个节点到底是属于哪个集合的?

需求2:现在,我想把A为根节点的集合与C为根节点的集合进行合并得到一个新的集合。

需求3:目前有两个节点,分别是H和I,我想直到这两个节点是否属于同一个集合?

以上三个需求,并查集都能够实现,这也是并查集的主要功能。

二、并查集的原理

并查集的操作,刚才也看了,好神奇,但是他的原理很简单,底层就是一个1维整形数组

那么怎样的设计能把这些集合区分开来呢?

设计规则如下:

1、数组某一个元素的下标,对应着一个集合中一个元素的编号

2、数组某一个元素的值要么是负整数,要么是大于等于0的整数

3、如果数组某一个元素的值时负整数,这个元素的下标就是某一个集合的根节点编号

这个元素的元素值(负整数)的绝对值,就是这个集合的元素个数

4、如果数组某一个元素的值时大于等于0的整数,其元素值,是这个元素父节点的下标

其下标就是这个元素本身在自己集合的元素编号。

空讲不好理解,下面,小编还是用一个李子来给大家介绍

把下面这三个集合放入并查集:

第一步,初始化数组arr(并查集):

长度就是所有集合元素之和,每一个元素赋值-1:

现在这个数组就可以理解为有10个集合(含负数的元素个数),

每个集合的大小都是1(负数绝对值代表集合的个数)

第二步,把集合存放到并查集(具体实现文章后面讲,先看结果):

第一次看到,这个感觉很抽象,你看,这怎么判断谁是谁的集合?

其实很简单,下面是并查集的核心理解了哦:

如果要查看下面这个5元素,是那个集合的:

那就在数组arr中找到5这个下标,5下标对应的值是2,那就跟着蓝色箭头,走到2小标对应位置。

发现arr[2]<0,那么下标2就是5这个元素所属集合的根节点编号,而arr[2]的绝对值就是集合的大小。

神奇吧。

其他的查询和上面讲的一模一样,自己可以动手试试。

三、并查集的JAVA实现

实现并查集主要就是实现一下三个目标:

1、给定一个元素,获取此元素所属集合的根节点

2、合并两个元素所属的两个集合

3、判断两个元素,是否属于同一个集合

代码:

public class UnionFindSet {
    public int[] elem;//底层用数组维护

    public UnionFindSet(int n) {//构造方法,设置数组长度,默认每一个元素值-1(都是一个一个的集合,只有自己一个元素)
        this.elem = new int[n];
        Arrays.fill(elem,-1);
    }


    /**
     * 查找数据x 的根节点(属于那个集合)
     * @param x
     * @return 下标
     */

    public int findRoot(int x) {

        if(x < 0) {
            throw new IndexOutOfBoundsException("下标不合法,是负数");
        }

        while (elem[x] >= 0 ) {//当值是负数时,找到集合的根结点
            x = elem[x];//1  0
        }

        return x;
    }

    /**
     * 查询x1 和 x2 是不是同一个集合
     * @param x1
     * @param x2
     * @return
     */
    public boolean isSameUnionFindSet(int x1,int x2) {

        /**
         * 获取根节点下标
         * */
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if(index1 == index2) {
            return true;
        }
        return false;
    }

    /**
     * 这是合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1,int x2) {//把x2合并到x1所属的集合

        /**
         * 获取根节点的下标
         * */
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);

        if(index1 == index2) {//同一个元素,不需要合并
            return;
        }

        elem[index1] = elem[index1] + elem[index2];
        elem[index2] = index1;
    }

    public int getCount() {//获取到底有几个集合
        int count = 0;
        for (int x : elem) {
            if(x <  0) {
                count++;
            }
        }
        return count;
    }



}

  • 47
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值