JavaDS —— 并查集

news2024/11/13 8:50:04

并查集概念

案例引入:
假设现在有三个程序设计小分队,分别来自广东,广西和海南,其中广东小分队人员的编号为{0,6,7,8}
广西小分队人员编号为{1,4,9},海南小分队人员编号为{2,3,5},每一支队伍的队长编号分别为0、1、2.

那么这里就有三个集合,我们可以用树形图来表示:根节点为各队的队长
在这里插入图片描述

如果大家都是同一支队伍的话,肯定是相互认识的,所以上面的树形图还可以用来表示人际交往关系图,只要两个人不是在同一个集合里面,那么就互不认识。

根据上面的树形图我们可以使用数组的形式来表示,首先初始化,每一个元素标记为 -1:
在这里插入图片描述

根据树形图,数组可以变成下面:
在这里插入图片描述

解释一下:如果元素是负数的话,说明这个元素对应的下标是根结点,相反就是其索引的双亲结点,元素如果是负数,负数的绝对值表示包含自己在内的树一共有多少个结点(例如:-4,说明包含根节点在内的树一共有 4 个结点)

所以在初始化将元素设置为 -1,是将索引的双亲结点默认为自身。


在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元素集
合,然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复用到查询某一个元素归属于那个集
合的运算
。适合于描述这类问题的抽象数据类型称为并查集(union-find set)

并查集实现

并查集底层使用的是数组,该数据结构的思想和树是类似的。

下面代码的 x 表示的是数组的下标索引

提供构造方法

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

这里使用了Arrays.fill方法,这个方法能将数组所有元素都填充为指定值。

查找根结点(重点)

根结点的标志性特征是元素值为负数,如果元素为正数,则需要一直向上寻找根结点,使用循环,先判断 elem[x] 是否为零,如果是则跳出循环,说明找到了最终的根结点,如果不是则需要继续查找,将 x 置为 elem[x]。

    //查找双亲结点
    public int findRoot(int x) {
        if(x < 0 || x >= elem.length) {
            throw new IndexOutOfBoundsException("x 不合法");
        }
        while(elem[x] >= 0) {
            x = elem[x];
        }
        return x;
    }

判断二者是否在同一个集合中

这个直接使用上面寻找根结点的方法来判断即可。

    //判断两个元素是否在同一个集合中
    public boolean isSameSet(int x1, int x2) {
        if(findRoot(x1) == findRoot(x2)) {
            return true;
        }
        return false;
    }

将两个元素合并到同一个集合中(重点)

首先两个元素如果就已经在同一个集合中的话,则直接返回即可。

如果不再同一个集合的话,我们需要先确定一件事情,并查集是互不相交的集合,意味着不能出现交集,也就是说,合并两个元素的本质其实就是将两个集合合并成一个集合。

既然要将两个集合合并成一个集合的话,我们就需要先找到两个集合的根节点。

然后将一个根节点作为最终集合的根结点,另一个根节点直接合并进去即可
在这里插入图片描述

我们以 S1 的根节点最为最终的根节点

在这里插入图片描述
那么我们要修改S1 根节点的数值,由于根节点的数值的绝对值表示这个集合一共有多少个结点,所以将原先两个集合的根节点的数值相加就是最终根节点的数值。

最后我们要修改 S2 根节点的数值,将其置为 S1 的根节点的索引值,这样这个结点就变成普通的集合结点了。

    //将两个元素合并到同一个集合中
    public void union(int x1, int x2) {
        if(isSameSet(x1,x2)) {
            return;
        }
        int r1 = findRoot(x1);
        int r2 = findRoot(x2);
        elem[r1] += elem[r2];
        elem[r2] = r1;
    }

统计集合个数

集合个数等于所有树的根节点,当元素值为负数时,则其对应的索引值就是根结点,我们直接遍历数组即可。

    //统计集合的个数
    public int setSizes() {
        int count = 0;
        for(int x : elem) {
            if(x < 0) {
                count++;
            }
        }
        return count;
    }

并查集最终代码

import java.util.Arrays;

public class UnionFindSet {

    public int[] elem;

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

    //查找双亲结点
    public int findRoot(int x) {
        if(x < 0 || x >= elem.length) {
            throw new IndexOutOfBoundsException("x 不合法");
        }
        while(elem[x] >= 0) {
            x = elem[x];
        }
        return x;
    }

    //判断两个元素是否在同一个集合中
    public boolean isSameSet(int x1, int x2) {
        if(findRoot(x1) == findRoot(x2)) {
            return true;
        }
        return false;
    }

    //将两个元素合并到同一个集合中
    public void union(int x1, int x2) {
        if(isSameSet(x1,x2)) {
            return;
        }
        int r1 = findRoot(x1);
        int r2 = findRoot(x2);
        elem[r1] += elem[r2];
        elem[r2] = r1;
    }

    //统计集合的个数
    public int setSizes() {
        int count = 0;
        for(int x : elem) {
            if(x < 0) {
                count++;
            }
        }
        return count;
    }
}

实战演练

省份数量

在这里插入图片描述

解析:
当某一些城市有着直接或者间接连接的时候,那么这些城市可以认定为一个省份,如果把省份当成一个集合的话,那城市就是集合的结点,所以这道题目我们可以使用并查集来解决。

首先并查集创建一个数组,大小为 n (城市的个数),然后开始遍历矩阵,当出现 1 的时候要进行两个元素(i,j)合并。

最后统计集合总数(即为省份总数)

在遍历矩阵的时候,我们其实可以只遍历矩阵沿着主对角线的一半,连对角线的元素也不用遍历,为什么呢?因为 A城市 与 B城市 连接的话 等价于 B城市 与 A城市 连接,在矩阵中沿着主对角线呈对称性,所以可以只遍历一半的矩阵

由于矩阵的主对角线的元素在并查集创建的时候,就已经进集合里了(即自己就是一个集合),所以主对角线也是可以不用遍历的。

class Solution {
    public int findRoot(int[] elem, int x) {
        while(elem[x] >= 0) {
            x = elem[x];
        }
        return x;
    }

    public void union(int[] elem, int x1, int x2) {
        int r1 = findRoot(elem, x1);
        int r2 = findRoot(elem, x2);
        if(r1 == r2) {
            return;
        }
        elem[r1] += elem[r2];
        elem[r2] = r1;
    }

    public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        int[] elem = new int[n];
        Arrays.fill(elem, -1);
        for(int i = 0; i < n; i++) {
            for(int j = i + 1; j < n; j++) {
                if(isConnected[i][j] == 1) {
                    union(elem,i,j);
                }
            }
        }

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

        return count;
    }
}

等式方程的可满足性

在这里插入图片描述

解析:
当变量存在相等关系的时候,则说明这些变量可以表示同一个整数,如果这些变量又存在不相等的关系的话,则无法进行表示,举个例子:a == b, a != b,你说这个方程有解吗?

如果我们把相等的元素存在集合里,然后再次遍历字符串数组,发现不相等的关系的时候,需要判断这两个元素是否在同一个集合中,如果不在同一个集合中的话,则没有问题,如果在同一个集合中,则说明等式方程存在错误。

class Solution {
    public int findRoot(int[] elem, int x) {
        while(elem[x] >= 0) {
            x = elem[x];
        }
        return x;
    }

    public void union(int[] elem, int x1, int x2) {
        int r1 = findRoot(elem, x1);
        int r2 = findRoot(elem, x2);
        if(r1 == r2) {
            return;
        }
        elem[r1] += elem[r2];
        elem[r2] = r1;
    }

    public boolean equationsPossible(String[] equations) {
        int[] elem = new int[26];
        Arrays.fill(elem, -1);
        int n = equations.length;
        for(int i = 0; i < n; i++) {
            if(equations[i].charAt(1) == '=') {
                union(elem, equations[i].charAt(0) - 'a', equations[i].charAt(3) - 'a');
            }
        }

        for(int i = 0; i < n; i++) {
            if(equations[i].charAt(1) == '!') {
                int r1 = findRoot(elem, equations[i].charAt(0) - 'a');
                int r2 = findRoot(elem, equations[i].charAt(3) - 'a');
                if(r1 == r2) {
                    return false;
                }
            }
        }

        return true;
    }
}

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

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

相关文章

关于WebZip乱码目录文件名修改

先引用一段 来描述问题&#xff1a; 在IT行业中&#xff0c;我们经常遇到与编码和字符集有关的问题&#xff0c;特别是在处理包含中文字符的文件或目录时。"WebZip乱码目录文件名修改"这个问题就是一个典型的例子&#xff0c;涉及到Webzip工具在下载包含中文路径的文…

清洁眼镜片

经常眼镜弄脏了 怎么弄都干净不了 根本无法佩戴 影响出门时间 有时在外很尴尬 其实清洁很简单 从水龙水冲洗镜片(可附上洗手液) 然后用纸巾局部点触抹干偶尔泛起小水滴 就好

计算机毕业设计SpringBoot+VUE自动灌装生产线 MES 系统设计

采用 B/S 架构&#xff0c;MES 应用软件通过 TCP/IP 协议与自动灌装生产线上的各个工作单元中的 PLC 控制器进行通信&#xff0c;查询或采集由 PLC 控制器采集的生产数据。通过 JAVA 构建的平台与数据库进行连接&#xff0c;实现灌装生产线的生产管理、订单管理、质量管理和数据…

DPDK基础入门(六):从PCIe事务的角度看包处理

PCIe PCI Express&#xff08;Peripheral Component Interconnect Express&#xff09;又称PCIe&#xff0c;它是一种高速串行通信互联标准。PCIe规范遵循开放系统互联参考模型&#xff08;OSI&#xff09;&#xff0c;自上而下分为事务传输层、数据链路层、物理层。对于特定的…

【Hot100】LeetCode—70. 爬楼梯

目录 1- 思路动规五部曲 2- 实现⭐763. 划分字母区间——题解思路 3- ACM 实现 原题链接&#xff1a;70. 爬楼梯 1- 思路 动规五部曲 1- dp 数组创建&#xff0c;确定含义 dp[i] 代表到达 楼梯 i 的方法数 2- 状态转移方程 因为一共有两种移动的方式&#xff0c;当前 dp[i] …

基于Java的垃圾分类网站系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架&#xff0c;B/S架构 工具&#xff1a;MyEclipse, Tomcat 系统展示 首页 用户管理…

Radmin-同一局域网只需IP就可以控制电脑

Radmin小编十多年前就在用&#xff0c;它是一款非常好用的局域网控制工具&#xff0c;可以完全替代Windows自带的远程桌面&#xff0c;它的安全性和便于操作性都比Windows的远程桌面好用。 Radmin还有一个好处&#xff0c;就是远程别人电脑时&#xff0c;对方那边毫无察觉&…

明明的随机数处理问题分析与解决方案

明明的随机数处理问题分析与解决方案 引言问题描述解决方案数据结构设计具体步骤伪代码C语言实现详细解释读取输入去重操作排序操作输出结果复杂度分析引言 明明生成了N个1到500之间的随机整数,我们需要对这些整数进行处理,删去重复的数字,然后进行排序并输出结果。本文将详…

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 哈希表这里用哈希集合Set()实现。左指针i&#xff0c;右指针j&#xff0c;从头遍历数组&#xff0c;若j指针指向的元素不…

jmeter性能测试HTML测试报告生成详解

作用&#xff1a;jmeter支持生成HTML测试报告&#xff0c;方便查看测试计划中获得图表和统计信息 命令&#xff1a; jmeter -n -t [jmx file] -l [result file] -e -o [html report folder] 示例&#xff1a;jmeter -n -t login.jmx -l result.jtl -e -o ./report jmx文件&a…

玛雅Maya2024下载安装教程影视三维3D设计教程百度网盘分享链接地址

玛雅Maya下载安装教程影视三维3D设计教程百度网盘分享链接地址,玛雅是一款3d设计软件。Maya 是由 Autodesk 公司开发的专业三维计算机图形软件。主要应用于影视特效制作、游戏开发、广告设计和工业设计等领域。在影视特效中&#xff0c;可创建逼真特效场景和角色动画&#xff1…

【Day09-IO-字符流其它流】

IO流 IO流-字符流 字节流&#xff1a;适合复制文件等&#xff0c;不适合读写文本文件 字符流&#xff1a;适合读写文本文件内容 FileReader&#xff08;文件字符输入流&#xff09; 作用&#xff1a;以内存为基准&#xff0c;可以把文件中的数据以字符的形式读入到内存中来。 …

husky 工具配置代码检查工作流:提交代码至仓库前做代码检查

提示&#xff1a;这篇博客以我前两篇博客作为先修知识&#xff0c;请大家先去看看我前两篇博客 博客指路&#xff1a;前端 ESlint 代码规范及修复代码规范错误-CSDN博客前端 Vue3 项目开发—— ESLint & prettier 配置代码风格-CSDN博客 husky 工具配置代码检查工作流的作…

高并发内存池(二):​整体框架的介绍与ThreadCache的实现

目录 整体框架介绍 ThreadCache的主体框架 自由链表-FreeList 内存对齐-RoundUp 计算桶位置-Index 基础版 进阶版 线程局部存储 __declspec(thread) 关键字 实现线程无锁 申请内存-Allocate 释放内存-Deallocate 从中心缓存中申请内存 整体框架介绍 高并发内存池…

变量数据类型 Day3

1. 变量 1.1 变量的概念 变量是计算机内存中的一块存储单元&#xff0c;是存储数据的基本单元变量的组成包括&#xff1a;数据类型、变量名、值&#xff0c;后文会具体描述变量的本质作用就是去记录数据的&#xff0c;比如说记录一个人的身高、体重、年龄&#xff0c;就需要去…

【微处理器系统原理和应用设计第十讲】外部中断之开发键控灯亮灭功能

一、基础知识 外部设备所产生的信号通过EXIT触发中断。 1、与中断相关的主要寄存器 EXTI共设有6个寄存器&#xff0c;分别为中断屏蔽寄存器&#xff08;IMR&#xff09;&#xff0c;事件屏蔽寄存器&#xff08;EMR&#xff09;&#xff0c;上升沿触发选择寄存器&#xff08;…

Ubuntu | 安装 Truffle 框架(安装缓慢)

目录 预备工作具体步骤Step1&#xff1a;安装 nvma. 官方方式&#xff08;可能失败&#xff09;b. 压缩包安装方式 Step2&#xff1a;安装 node.js 和 npmStep3&#xff1a;安装 Truffle 参考博客 前言&#xff1a;昨天安装 Truffle 框架&#xff0c;结果缓冲条转了一晚上都没安…

利士策分享,如何平衡物质追求与心理健康?

利士策分享&#xff0c;如何平衡物质追求与心理健康? 在快节奏的现代社会&#xff0c;物质追求与心理健康仿佛成了人们生活中不可或缺的两极。 一方面&#xff0c;科技的飞速发展和经济的繁荣让我们拥有了前所未有的物质享受&#xff1b; 另一方面&#xff0c;高压的工作环…

前端基础 | HTML基础:HTML结构,HTML常见标签

文章目录 HTML1、HTML结构1.1HTML标签1.1.1标签1.1.2标签含义 1.2HTML文件基本结构1.3标签层次结构1.4 快速生成代码框架 2、HTML常见标签2.1注释标签2.2标题标签&#xff1a;h1–h62.3段落标签&#xff1a;p2.4 换行标签&#xff1a;br2.5格式化标签2.6 图片标签&#xff1a;i…

细致刨析JDBC ② 进阶篇

目录 一、JDBC拓展 1.实体类和ORM Ⅰ、ORM思想封装单个对象 Ⅱ、ORM思想封装集合 2.主键回显 3.批量操作 ① 循环逐条数据进行添加 ② 批量进行添加 二、连接池 1.现有问题 2.连接池 3.常见连接池 4.Druid连接池使用 使用步骤&#xff1a; 硬编码 软编码 5.HikariCP连接池使用 …