【数据结构进阶】并查集

news/2024/11/8 20:32:36/

并查集

正如它的名字一样,并查集(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.ppmy.cn/news/9913.html

相关文章

java实现的非关系型数据库:nosqldb

nosqldb一、nosqldb介绍二、nosqldb功能介绍三、数据存储结构介绍1. 数据文件存储结构(data.nosqldb)2.索引文件存储结构(index.mbdb)三、优化点1. 不支持连表查询2. 不支持分片存储3. 碎片整理一、nosqldb介绍 github地址 https://github.com/MaBo2420935619/nosqldb nosqld…

零基础学MySQL(一)-- 启动与创建数据库及对数据库的备份与恢复

&#x1f9e7;启动与创建数据库及对数据库的备份与恢复&#x1f957;一、启动与连接数据库1️⃣启动数据库2️⃣连接数据库&#x1f96b;二、数据库的基本介绍1️⃣数据库的三层结构2️⃣数据在数据库中的存储方式3️⃣SQL 语句分类&#x1f371;三、对数据库的操作1️⃣创建数…

Neo4j详细介绍及使用教程

文章目录一、Neo4j介绍1.Neo4j简介2.图数据库简介3.Neo4j的优缺点4.Neo4j的常见应用场景二、使用教程1.下载安装2.数据插入和查询(1)基本概念(2)基本语法Ⅰ.CREATE操作——创建Ⅱ.MERGE——创建或更新Ⅲ.Match操作——查找指定的图数据Ⅳ.DELETE操作——删除节点3.JAVA实战一、…

功率二极管的损耗分析和选型原则

功率二极管的损耗分析和选型原则 tip&#xff1a;参考网上资料&#xff0c;学习为主 1.二极管的分类 2.二极管的损耗组成 3.二级管的损耗分析 4.应用实例1.Flyback电源电路二极管损耗计算 5.实例应用2.BOOST电路二极管损耗计算 6.实例应用3.大功率整流桥二极管参数计算 7.选型…

Golang面试

简单golang里的数组和切片有了解过吗数组和切片都是拥有相同类型一组元素集合数组为固定长度&#xff0c;切片为可变长度数组不可扩容&#xff0c;切片可以&#xff0c;切片扩容&#xff0c;如果不足1024每次扩容为两倍扩容&#xff0c;如果高于1024&#xff0c;为1.25倍扩容切…

正则表达式总结,正则表达式匹配不包含某个字符串

1、匹配a标签及其url&#xff1a; Regex regA new Regex("<a[\s][^<>]*href(?:""|)([^<>""])(?:""|)[^<>]*>([^<>])</a>", RegexOptions.IgnoreCase); 说明&#xff1a;在上面的正则表达式中…

【bioinfo】酶切法片段化建库相比超声打断建库引入softclip使用FADE软件识别/去除

FADE软件参考文献 参考文献&#xff1a;片段化酶诱导的双链伪影的表征和缓解 - PMC (nih.gov) 文献提供的酶切产生的错误识别和去除软件FADE&#xff1a;软件git地址 文献补充材料图&#xff1a;由酶切产生的人工错误序列的碱基质量值偏高&#xff0c;相对其他softclip的质量…

【库函数】-了解回调函数,并且手把手带你学习qsort函数!!还不知道的赶快进来看看

&#x1f387;作者&#xff1a;小树苗渴望变成参天大树 &#x1f389;作者宣言&#xff1a;认真写好每一篇博客 &#x1f38a;作者gitee:link 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; qsort&#x1f9e8; 前言✨一、什么是回调函数…