java并查集算法带你领略热血江湖
目录
- 一、什么是并查集
- 二、深入理解并查集
- 三、实现并查集
- 四、真题训练
- 五、路径压缩优化
- 六、总结
校招收获数十个offer,年薪均20W~40W。
感谢茫茫人海中我们能够相遇,
俗话说:当你的才华和能力,不足以支撑你的梦想的时候,请静下心来学习,
希望优秀的你可以和我一起学习,一起努力,实现属于自己的梦想。
一、什么是并查集 并查集被很多OIer认为是最简洁而优雅的数据结构之一,主要用于处理一些不相交集合的合并问题,并支持两种操作:
- 合并(Union):把两个不相交的集合合并为一个集合。
- 查询(Find):查询两个元素是否在同一个集合中。
二、深入理解并查集 在遥远的江湖,有一群武侠人,他们各自为战,守护着自己该守护的东西。
- 1号:擅长刀、武力值98
- 2号:擅长剑、武力值95
- 3号:擅长棍、武力值93
- 4号:擅长枪、武力值90
- 小黄:擅长机关枪、武力值100、宝物:并查集
- 箭头的含义:自己的大哥(比如2号的箭头指向自己,那2号的大哥就是他自己)
文章图片
小黄看到其余4人的资料后,使用宝物并查集查询了一下4号和其余几人的关系,发现4号是孤胆一人, 决定从4号先入手。
在一个月黑风高的晚上,小黄拿着机关枪,使用宝物并查集的合并,把4号进行收服,成为其小弟。
1号、2号、3号听闻昨夜4号被收服,立刻举行会议商讨合作的事情,1号由于身高气傲,不想要与另外两人合作,2号、3号只好进行合作。
于是,江湖的局面又发生了变化。
文章图片
小黄的野心可是称霸江湖,于是使用宝物并查集分别查询了1号、2号、3号的目前状态,得知:1号还是独胆一人,而2、3号已经结盟。
小黄又在一个夜黑风高的晚上与1号大战一夜,最终使用宝物并查集的合并,收服了1号。
至此,江湖只存在两个派系,2号派系、小黄派系。
2号察觉到了目前小黄的势力,主动摆宴,希望江湖不在打打杀杀,能够平安无事。
在宴上,由于2号派系的人数小于小黄派系的人数,于是,2号派系正式被小黄派系吞噬,江湖正式统一。
文章图片
三、实现并查集 并查集的初始化:
class UnionAndFind() {// 自己的大哥是谁private int[] parent; // 自己的队伍有多少人private int[] size; // 江湖还有几个派系int sets; // 初始化// 每个人的大哥都是自己// 每个队伍的人数都为1public UnionAndFind(int N) {parent = new int[N]; size = new int[N]; help = new int[N]; sets = N; for (int i = 0; i < N; i++) {parent[i] = i; size[i] = 1; }}}
并查集的 查询(Find) 方法:这里的时间复杂度可以降低到O(1)级别,不敢相信吧,那就往下看吧。
// 查询a和b在不在一个阵营中 public boolean isSameSet(int a, int b) {return find(a) == find(b); } // 看一下你的最终大哥是谁public int find(int i){while(i != parent[i]){i = parent[i]; }return i; }
并查集的 合并(Union)方法:先去找到两个参数的最终大哥,小弟少的大哥跟着小弟多的大哥混
public void union(int a, int b) {int A = find(a); int B = find(b); if (A != B) {if (size[A] >= size[B]) {size[A] += size[B]; parent[B] = A; } else {size[B] += size[A]; parent[A] = B; }sets--; }}
四、真题训练 547. 省份数量
初始化数组,拷贝上述并查集即可
public int findCircleNum(int[][] isConnected) {int N = isConnected.length; UnionAndFind unionFind = new UnionAndFind(N); for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {if (isConnected[i][j] == 1 && i != j) {unionFind.union(i, j); }}}return unionFind.sets; }
文章图片
五、路径压缩优化 我们目前的并查集已经可以解决大部分问题,但是,我们能够对目前的并查集版本进行路径压缩优化。
我们可以发现,在我们 find() 方法时,我们会进行一个 while() 循环的查找大哥的操作,这种操作十分的浪费时间,有没有什么办法可以进行下优化呢?
如下所示:
文章图片
我们可以看到对于6、5、3之类来说,每次进行 find 查询的时候,都需要经过2~3次的循环才能够找到1。
如果我们在进行查找的时候,保存一下值,然后重新挂到1的上面,比如:
我要查询下6,肯定会查询找5、3,这个时候,我们把6、5、3保存到临时数组中,在查询完毕后,将这些数组直接挂到1的下面,这样的话,下次查询会降低循环的次数。
文章图片
当我们查询的次数远远大于我们的数据量时,这个时候 find() 的操作就已经变成了一个O(1)级别的查询时间。
public int find(int i) {int h = 0; while (i != parent[i]) {help[h++] = i; i = parent[i]; }for (h--; h >= 0; h--) {parent[help[h]] = i; }return i; }
六、总结 并查集算法通常用在处理一些不交集(Disjoint Sets)的合并及查询问题
力扣也有并查集的 tag 专栏:并查集
大家可以好好体会一下路径压缩的奇妙,通过不断的路径压缩,可以将我们的时间复杂度降低到O(1)
这里可能有小伙伴有疑问,为啥是O(1),你就算挂在1的下面,不也得查询两次嘛
对于时间复杂度来说,只要是常数的次数都可以认定为O(1)的时间复杂度,况且,你也可以使用HashMap来进行存储。
【java并查集算法带你领略热血江湖】以上就是java并查集算法带你领略热血江湖的详细内容,更多关于java并查集算法的资料请关注脚本之家其它相关文章!
推荐阅读
- Java实现并查集示例详解
- JavaScript|JavaScript WebGL 矩阵
- Java|Java Exception异常全方面分析
- 别再写垃圾代码了(试试阿里巴巴 Java 开发插件,打造你的团队专属风格。。。)
- java学习|喜欢看球,那就手撸一个看球小程序系统
- 如何理解Node.js和JavaScript的关系
- java|面试官: 说说看, 什么是 Hook (钩子) 线程以及应用场景?
- java|面试官(ZK(ZooKeeper)分布式锁实现,你了解了吗())
- Java|面试官问(说说你对ZooKeeper集群与Leader选举的理解())
- 论文回顾(Batch Augmentation,在批次中进行数据扩充可以减少训练时间并提高泛化能力)