【数据结构进阶】并查集

news2025/6/23 12:01:33

并查集

正如它的名字一样,并查集(Union-Find)就是用来对集合进行 合并(Union)查询(Find) 操作的一种数据结构。
合并 就是将两个不相交的集合合并成一个集合。
查询 就是查询两个元素是否属于同一集合。

并查集的原理

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。

组成

并查集主要由一个整型数组pre[ ]和两个函数find( )、join( )构成。

数组 pre[ ] 记录了每个点的前驱节点是谁,函数 find(x) 用于查找指定节点 x 属于哪个集合,函数 join(x,y) 用于合并两个节点 x 和 y 。

现实意义

比如现在有这样的情况:

目前饭局有10个人,没两个人之间都可能存在远方亲戚关系,如何将个人分成不同的家庭?现在将个人编号为{0,1,2,3,4,5,6,7,8,9}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xp2EqcYB-1672971490156)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672967653754.png)]

-1表示假设每个人之间都不存在亲戚关系

接下来,饭局中的人互相交谈,了解到自己的亲戚关系,因此可以形成不同的家庭。比如:当前是s1:{0,6,7,8},s2:{1,4,9},s3:{2,3,5}形成了不同的家庭。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VZrorsUR-1672971490158)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672967711197.png)]

用树模型表示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8NEgt0JH-1672971490159)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672967778007.png)]

因此规定并查集数组有下面的规定:

  1. 数组的下标对应集合中元素的编号
  2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数
  3. 数组中如果为非负数,代表该元素双亲在数组中的下标
  4. 如果此时0和1发现也有亲戚关系,那么s1和s2可以合并为一个新的家庭

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-04KJOESp-1672971490160)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672967818968.png)]

并查集需要有下面的接口

  • 查找元素属于哪个集合
  • 查看两个元素是否属于一个集合
  • 合并两个集合
  • 集合的个数

并查集的实现

创建一个并查集

并查集底层就是一个数组,数组内每个元素初始为-1。

public class unionfindset {
    public int[] elem;
    public int usedSize;

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

    public static void main(String[] args) {
        unionfindset unionfindset = new unionfindset(10);
        System.out.println(123);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1R8vPpbF-1672971490160)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672968167815.png)]

功能实现:

/**
     * 查找数据 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;
        }else{
            return false;
        }
    }
 /**
     * 这是合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1,int x2){
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return;
        }else{
            elem[index1] = elem[index1]+elem[index2];
            elem[index2]=index1;
        }

    }

    public int getCount(){
        int count = 1;
        for (int x:elem){
            if (x<0){
                count++;
            }
        }
        return count;
    }

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

并查集的应用

省份数量

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

来源:力扣(LeetCode)
链接:省份数量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bHwZJlCa-1672971490161)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672971156494.png)]

class Solution {
   public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        unionfindset ufs = new unionfindset(n);
        for(int i = 0;i < isConnected.length;i++) {
            for(int j = 0;j < isConnected[i].length;j++) {
                if(isConnected[i][j] == 1) {
                    ufs.union(i,j);
                }
            }
        }
        return ufs.getCount();
    }

}
class unionfindset {
    public int[] elem;
    public int usedSize;

    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]; //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;
        }else{
            return false;
        }
    }

    /**
     * 这是合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1,int x2){
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return;
        }else{
            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;
    }

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

等式方程的可满足性

给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。

只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。

来源:力扣(LeetCode)
链接:等式方程的可满足性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FGoEk71r-1672971490162)(C:\Users\17512\AppData\Roaming\Typora\typora-user-images\1672971191283.png)]

class Solution {



     //等式的满足性
    public boolean equationsPossible(String[] equations) {
        unionfindset ufs = new unionfindset(26);
        for(int i = 0; i < equations.length;i++) {
            if(equations[i].charAt(1) == '=') {
                ufs.union(equations[i].charAt(0)-'a',equations[i].charAt(3)-'a');
            }
        }
        for(int i = 0; i < equations.length;i++) {
            if(equations[i].charAt(1) == '!') {
                int index1 = ufs.findRoot(equations[i].charAt(0)-'a');
                int index2 = ufs.findRoot(equations[i].charAt(3)-'a');
                if(index1 == index2) return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        String[] str = {"a==b","b!=a"};
        //equationsPossible(str);
    }

}
class unionfindset {
    public int[] elem;
    public int usedSize;

    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]; //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;
        }else{
            return false;
        }
    }

    /**
     * 这是合并操作
     * @param x1
     * @param x2
     */
    public void union(int x1,int x2){
        int index1 = findRoot(x1);
        int index2 = findRoot(x2);
        if (index1==index2){
            return;
        }else{
            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;
    }

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

链表常见OJ题汇总(持续更新)

目录前言一、移除链表中的元素&#xff08;多指针法&#xff09;二、反转链表&#xff08;多指针法&头插法&#xff09;三、链表的中间结点&#xff08;算数法和双指针法&#xff09;四、链表中的第K个结点&#xff08;算数法&双指针法&#xff09;五、合并两个有序链表…

vue 父子组件设置 scoped, 如何导致滚动条失效的

vue父组件的页面结构 // 调用子组件 <process-time-line :nodeArr"nodeArr"></process-time-line> 父组件的样式 <style lang"scss" scoped> ::-webkit-scrollbar {width: 0px;height: 0px;} </style>子组件的页面结构 <div …

学习C语言笔记:字符串和格式化输入/输出

学习内容&#xff1a;1.函数——strlen()&#xff1b;2.关键字——const&#xff1b;3.字符串&#xff1b;4..如何创建、存储字符串&#xff1b;5.如何使用strlen()函数获取字符串的长度&#xff1b;6.用C预处理器指令#define和ANSIC的const修饰符创建符号常量。与程序交互和使…

《Linux运维实战:Centos7.6基于docker-compose一键离线部署redis6.2.8之哨兵集群》

一、部署背景 由于业务系统的特殊性&#xff0c;我们需要面向不通的客户安装我们的业务系统&#xff0c;而作为基础组件中的redis针对不同的客户环境需要多次部署哨兵集群&#xff0c;作为一个运维工程师&#xff0c;提升工作效率也是工作中的重要一环。所以我觉得有必要针对re…

(Java高级教程)第三章Java网络编程-第一节3:网络编程必备网络知识3之IP地址、端口号

文章目录一&#xff1a;网络传输基本流程&#xff08;1&#xff09;数据包&#xff08;2&#xff09;网络传输的基本流程&#xff08;3&#xff09;具体处理过程A&#xff1a;发送数据B&#xff1a;路由转发C&#xff1a;接受数据二&#xff1a;网络中的地址&#xff08;1&…

Elasticsearch-使用入门

_cat /_cat/nodes&#xff1a;查看所有节点 接口&#xff1a;GET http://192.168.177.134:9200/_cat/nodes /_cat/health&#xff1a;查看ES健康状况 接口&#xff1a;GET http://192.168.177.134:9200/_cat/health /_cat/master&#xff1a;查看主节点信息 接口&#xff1a;G…

【Azure 架构师学习笔记】-Azure Logic Apps(3)-演示1

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Logic Apps】系列。 接上文【Azure 架构师学习笔记】-Azure Logic Apps&#xff08;2&#xff09;-组件介绍 前言 前面两篇文章大概介绍了一些理论知识&#xff0c;但是为用而学才是最重要的&#xff0c;所以接下来做…

word排版时如何保证每张图片大小一样?

问题描述 为了保证文档的美观性&#xff0c;在对图片进行排版时&#xff0c;最好保证图片的大小一致&#xff0c;尤其是多张图片组成一张大图时。 如果一张张图片调整大小&#xff0c;那真的是毫无技术含量的耗时工作。 解决方案 在这提出一种借助表格的解决办法。比如有4张…

Parasoft发布最广泛的MISRA规则覆盖-C/C++test最新版本正式上线!

作为拥有30多年自动化软件测试经验的全球领导者Parasoft宣布发布Parasoft C/Ctest的最新2022.2版本&#xff0c;支持MISRA C:2012修正案3和MISRA C 202x的草案版本。Parasoft针对C和C软件开发的统一、完全集成的测试解决方案的最新版本&#xff0c;帮助团队实现自动化静态分析和…

【java入门系列三】java基础-控制结构

学习记录&#x1f914;分支控制if-elseswitch分支接收字符for循环控制while循环do-while打印金字塔break终止-可以用label&#xff1a;表明continue与break类似return循环中表示直接退出方法(函数)&#xff0c;主方法直接结束字符串比较trick讨论总结谢谢点赞交流&#xff01;(…

外观模式

外观模式 1.外观模式介绍 1.外观模式&#xff08;Facade&#xff09;&#xff0c;也叫“过程模式&#xff1a;外观模式为子系统中的一组接口提供一个一致的界面&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用 2.外观模式通过定义一个一…

Linux(06)之获取内核代码

Linux(06)之获取内核代码 Author&#xff1a;OnceDay Date&#xff1a;2023年1月5日 漫漫长路&#xff0c;有人对你微笑过嘛… 参考文档&#xff1a; 《Linux内核设计和实现》 1.概述 linux内核的基本架构如下&#xff1a; 所以每个处理器运行的地方只有以下可能&#xf…

带你玩转指针——指针进阶(二)

上次我们说到了函数指针&#xff0c;对于函数指针大家还不太清楚的参考&#xff0c;指针进阶&#xff08;一&#xff09;http://t.csdn.cn/z5cjM函数指针数组数组是存放相同类型的空间&#xff0c;前面我们已经学习了指针数组int* arr[10] 每个元素是int*那么我们把函数的地址存…

grpc实现c++异步非阻塞stream

grpc实现c异步非阻塞stream 参考文章 Non-blocking single-threaded streaming C servergRPC C async api doc and sample codegrpc异步stream server端demo 序言 原来一直是用着同步阻塞的grpc stream。由于不想再创建新的线程来监听grpc stream的新消息了&#xff0c;所以就…

怎么提高程序设计能力?可以参考程序-设计原则,程序-设计模式

怎么提高程序设计能力&#xff1f; 简单说下我的方式方法&#xff1a; 【程序架构】 借鉴设计模式和设计原则 【程序业务】 多理解客户需求&#xff0c;理解后&#xff0c;做竞品逻辑分析&#xff0c;分析出其逻辑结构&#xff0c;和数据结构 &#xff1b; 再根据客户需求…

自己有工厂,怎样接外贸订单?

很多做外贸的小工厂和小型加工厂&#xff0c;除了传统的营销渠道外&#xff0c;也不知道如何做、才能接到外贸订单。小工厂想获得外贸订单&#xff0c;可通过以下7个方法&#xff1a;1、注册一些外贸B2B平台&#xff0c;发布产品&#xff0c;等待客户询盘外贸B2B平台太多了&…

商务车改装之奔驰威霆改装

今天来看看这台车的改装效果&#xff0c;首先外观改成GLS的一个包围。同时大灯换了一个三道杠的运动大灯。运动大灯加上包围&#xff0c;是不是时尚了很多。再来看看威霆内饰&#xff0c;白红相间的色彩搭配&#xff0c;仪表台换成一个大连屏的仪表台&#xff0c;带着飞机一样的…

4路DI开关检测计数器NPN/PNP输入,Modbus TCP协议,WiFi模块YL160频率测量 计数器

特点&#xff1a;● 4路开关量输入&#xff0c;支持NPN和PNP输入● DI每一路都可用作计数器或者频率测量● 支持Modbus TCP 通讯协议● 可以设置每转脉冲数用于转速测量● 内置网页功能&#xff0c;可以通过网页查询电平状态● 可以通过网页设定输出状态● 宽电源供电范围&…

OPC Expert v8.1.2211 Crack

像专业人士一样解决您的 OPC 和 DCOM 连接问题 [无需经验] 快速修复 OPC 和 DCOM 错误 使用 OPC Expert&#xff0c;您无需任何经验即可解决和修复 OPC 连接问题。OPC Expert 为您完成繁重的工作&#xff0c;以快速自动诊断 OPC 和 DCOM 问题……Ω578867473而且还不止于此。OP…

基于Java+SpringBoot+vue+element实现餐厅点餐系统平台

基于JavaSpringBootvueelement实现餐厅点餐系统平台 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录基于JavaSpringBo…