当前位置: 首页 > news >正文

并查集是什么?怎么模拟实现?如何应用?

目录

一、什么是并查集?

二、并查集可以解决哪些问题?

三、并查集的模拟实现

3.1、并查集的定义

3.2、查询两个元素是否是同一个集合

3.3、合并两个集合

3.4、求集合个数

3.5、并查集完整代码

小结


一、什么是并查集?

        我们可以想象这样一个过程,开始时有n个元素,某些元素开始和其他元素按照一定规律进行集合合并,有可能就会分成几个集合。在这个过程中要反复某个元素是哪个集合的,这样的运算,被抽象成数据类型叫做“并查集”;

还是不太理解?举个例子

        例如:现在有10个人,分别对其进行编号,下标都为-1(为什么是-1,后面会解释),如下图:

 

现在将这个一个个零散人组建成如下三个团体:

 他们的下标被修改为:

 解释:

1. 数组的下标对应集合中元素的编号

2. 数组中如果为负数,负号表示当前下标为根结点,数字代表该集合中元素个数

3. 数组中如果为非负数,他的下标代表该元素的双亲结点

若继续合并,分成了如下两个小团体,数组变化如下:

 


二、并查集可以解决哪些问题?

通过以上栗子,可以发现,并查集可以解决以下问题:

1.查找元素属于哪个集合

方法:通过树一直找到根,也就是数组中的负数

2.查看两个元素是否属于同一个集合

方法:通过树一直找到根,若根相同说明是同一集合;

3.将两个集合合并成一个集合

方法:将一个集合名称改成另一个集合名称;

4.求集合个数

方法:数组中元素为负数是当前下标集合的个数;


三、并查集的模拟实现

3.1、并查集的定义

底层是一个数组,并且全部初始化为-1;

public class UnionFindSet {
    public int[] elem;

    public UnionFindSet(int n) {
        this.elem = new int[n];
        Arrays.fill(elem, -1);
    }
}

3.2、查询两个元素是否是同一个集合

这里实现很简单,只需要比较两个元素的根节点是否相同即可,那么就需要先找到两个根节点,可以通过一个循环进行查找,当查找到的元素小于0时,说明找到根节点了;

    /**
     * 查找数组 x下标是否是根节点
     * @param x
     * @return
     */
    public int findRoot(int x) {
        if(x < 0) {
            throw new IndexOutOfBoundsException("下表不合法,是负数");
        }
        while(elem[x] >= 0) {
            x = elem[x];
        }
        return x;
    }

    /**
     * 查询 x1 和 x2 是不是同一个集合
     * @param x1
     * @param x2
     * @return
     */
    public boolean isSameUnionFindSet(int x1, int x2) {
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        return index1 == index2;//跟结点是否相同
    }

3.3、合并两个集合

这里首先需要找到需要合并的两个元素的根节点,只有根节点不同才能进行合并,怎么合并呢?就是修改两个值,元素一的值因该被修改为这两个元素的和(这是更新孩子的数量),元素二的值因该被修改为元素一的下标(这是跟新另一颗树的根结点)(这里也很简单,建议参照上面我画的图去对比);

    /**
     * 合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1, int x2) throws Exception {
        //先找到 x1, x2 的根节点
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if(index1 == index2) {
            return;
        }
        elem[index1] = elem[index1] + elem[index2];
        elem[index2] = index1;
    }

3.4、求集合个数

遍历数组,并用一个计数器记录负数的个数,就是集合的个数;

    /**
     * 集合个数
     * @return
     */
    public int getCount() {
        int count = 0;
        for(int x : elem) {
            if(x < 0) {
                count++;
            }
        }
        return count;
    }

3.5、并查集完整代码

import java.util.Arrays;

public class UnionFindSet {
    public int[] elem;

    public UnionFindSet(int n) {
        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];
        }
        return x;
    }

    /**
     * 查询 x1 和 x2 是不是同一个集合
     * @param x1
     * @param x2
     * @return
     */
    public boolean isSameUnionFindSet(int x1, int x2) {
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        return index1 == index2;//跟结点是否相同
    }

    /**
     * 合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1, int x2) {
        //先找到 x1, x2 的根节点
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if(index1 == index2) {
            return;
        }
        elem[index1] = elem[index1] + elem[index2];
        elem[index2] = index1;
    }

    /**
     * 集合个数
     * @return
     */
    public int getCount() {
        int count = 0;
        for(int x : elem) {
            if(x < 0) {
                count++;
            }
        }
        return count;
    }

    public void print() {
        for(int x : elem) {
            System.out.print(x + " ");
        }
        System.out.println();
    }

    /**
     * 测试
     * @param args
     */
    public static void main(String[] args) {
        UnionFindSet unionFindSet = new UnionFindSet(10);
        System.out.println("合并:0 和 6:");
        unionFindSet.union(0, 6);
        System.out.println("合并:0 和 7:");
        unionFindSet.union(0, 7);
        System.out.println("合并:0 和 8:");
        unionFindSet.union(0, 8);

        System.out.println("合并:1 和 4:");
        unionFindSet.union(1, 4);
        System.out.println("合并:1 和 9:");
        unionFindSet.union(1, 9);
        System.out.println("合并:2 和 3:");
        unionFindSet.union(2, 3);
        System.out.println("合并:2 和 5:");
        unionFindSet.union(2, 5);
        unionFindSet.print();

        System.out.println("合并:8 和 1:");
        unionFindSet.union(8, 1);
        unionFindSet.print();
        System.out.println(unionFindSet.isSameUnionFindSet(6, 9));
        System.out.println(unionFindSet.isSameUnionFindSet(8, 2));
    }
}

小结

面试考的少,自行斟酌~


 

相关文章:

  • 一起做网店的类似网站/做推广的都是怎么推
  • 思帽西宁网站建设/怎么做小说推广挣钱
  • 成都网站建设策划/百度指数只能查90天吗
  • 怎样做视频上网站赚钱/口碑最好的it培训机构
  • 建设网站设计专业服务/找培训机构的app
  • 网站建站分为两种/怎么去做网络推广
  • 同源策略和跨域请求的实现
  • Python-import导入上级目录文件
  • 1592_AURIX_TC275_PMU_部分安全措施
  • 彻底分析Arduino库安装和开发板库安装路径和方式
  • GO 语言 Web 开发实战一
  • 区块链技术3--BTC协议
  • WAL Write AheadLog
  • 过气明星组合大衣哥、李嘉明、唐磊,谁录制祝福视频能价值100万
  • 明清专题数据库:企业匹配官办书局距离、科举考试、商帮文化变量等
  • 【零基础】学python数据结构与算法笔记12
  • 使用混沌和非线性控制参数来提高哈里斯鹰优化算法的优化性能,解决车联网相关的路由问题(Matlab代码实现)
  • 【阶段四】Python深度学习06篇:深度学习项目实战:卷积神经网络进行狗狗图像分类项目