目录

一、什么是并查集?

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

三、并查集的模拟实现

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));}
}

小结

面试考的少,自行斟酌~


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

  1. 并查集 ---- 扩展域并查集判二分图 + 循环模拟字典树 The 2020 ICPC Asia Macau Regional Contest C. Club Assignment (详解)

    题目链接 题目大意: 有n个数,现在要把他们拆分成两个集合,假设S为集合,有如下定义: f(S)={min(x⊕y)∣x,y∈S,andx!=y}f(S)=\{min(x\oplus y)|x,y\i ...

  2. HDU-3974 Assign the task 线段树 或 直接模拟多叉树 或 并查集 (三种方法)

    题目大意 t 组数据(t<=10),每组第一行一个 n 表示 n 个员工(n<=5e4),接下来 n-1 行,每行两个整数 u,v 表示 v 是 u 的上司 然后一行 m 表示有 m 个操 ...

  3. Day10 堆排序、模拟堆 trie树(字典树) 并查集

    堆呢就是一棵树完全二叉树... 小根堆的话,根节点就是最小值 维护堆只有两个操作 up(k) down(k) cnt是堆的大小 建堆的话只需要把前n/2的数down下来就ok 复杂度是小于O(n) 的 ...

  4. zcmu 1603 卡斯丁狗的战舰帝国(并查集+模拟)

    思路:看到题目我只知道,普通的暴力模拟是不行的,但是不知道用什么办法优化,然后看到网上的大佬们用了并查集恍然大悟: 用并查集维护每条船攻击后的根节点,最后输出查询位置的根节点就行,如果在期间存在该位置 ...

  5. 【洛谷P4997】不围棋【并查集】【模拟】

    题目大意: 题目链接:https://www.luogu.org/problemnew/show/P4997 「不围棋」是一种非常有趣的棋类游戏. 大家都知道,围棋的「气」是指一个棋子所在的联通块相邻 ...

  6. 【并查集】【模拟】家谱

    题目: 现代的人对于本家族血统越来越感兴趣, 现在给出充足的父子关系, 请你编写程序找到 某个人的最早的祖先. 输入: 输入文件由多行组成, 首先是一系列有关父子关系的描述, 其中每一组父子关系由二行 ...

  7. jzoj5904. 【NOIP2018模拟10.15】刺客信条(并查集)

    5904. [NOIP2018模拟10.15]刺客信条 Description 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受到圣殿骑士的杀害,决心成为一 ...

  8. 【牛客NOIP模拟】牛半仙的魔塔(增强版)【贪心】【并查集】

    题意:一个魔塔游戏的地图是一棵以 111 为根的树,起点为根,除根外每个结点有一个怪物,给定每个怪物血量.攻击.防御.奖励蓝宝石个数(加防御),勇士的血量.攻击.防御,遇到怪物必须战斗,勇士永远先手, ...

  9. jzoj4382-[GDOI2016模拟3.11]历史【并查集】

    正题 题目大意 有若干个事件 Kc:cK\ c:cK c:c值更改并且清除生气状态 Rxy:R\ x\ y:R x y:新建一条x′x'x′到y′y'y′的边,若当前处于生气状态则x=(x′+n−c) ...

最新文章

  1. epoch如何设置_使用TFRecordDataset时如何设置epoch计数器?
  2. 编程一个最简单游戏_一个关于AI编程的游戏
  3. java 内置函数_java8 四大内置核心函数式接口
  4. 每次登陆都要滑动验证_湖人队冠军成员卡鲁索很吃香:每次谈判都有N支球队点名要他...
  5. 深度学习《Transfer Learning》
  6. HTML+Javascript制作拼图小游戏详解(一)
  7. 订单明细升级版、淘宝erp、淘宝打单发货接口
  8. 路由器的下一跳计算(网关)
  9. UVA - 12304 2D Geometry 110 in 1!
  10. WPF中的文字修饰——上划线,中划线,基线与下划线
  11. uni-app小程序利用动画做出左右滑动切换的效果
  12. vs2015安装Qt插件问题
  13. ElasticSearchTransportClient集成SearchGuard插件实现索引级别的权限管控
  14. Syncthing文件同步方案完全攻略(亲测有效)
  15. php判断百度ua展示不同页面,通过UA或反查IP两种方法识别百度蜘蛛【官方说法】...
  16. 怎么提高百度快照更新的频次?
  17. Oracle 特殊符号‘‘的处理
  18. QRC:extract RC流程
  19. Java 实现 YoloV7 目标检测
  20. 分成两半的子爵读后感

热门文章

  1. SDUT--OJ《数据结构与算法》实践能力专题训练6 图论
  2. 台式电脑主机与屏幕的连接线
  3. Amdahl定律和Gustafson定律
  4. Android 全景视频播放器(VR视频播放器探索二)
  5. 2021-04-15 kk日记,415系统支持战况总结
  6. python求反余弦_python数学.acos反余弦问题
  7. vue+element 将密码输入框的小圆点显示为星号
  8. 基于Android的家校互动系统app
  9. C#链接SQL知识点
  10. Uniapp开发仿哔哩哔哩/短视频系统APP源码