java并查集算法带你领略热血江湖

网友投稿 244 2022-09-16


java并查集算法带你领略热血江湖

目录一、什么是并查集二、深入理解并查集三、实现并查集四、真题训练五、路径压缩优化六、总结

你好,我是小黄,一名独QIGBkTSOLv角兽企业的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;

// 初始化

// 每个人的大哥都是自己

// 每个队伍的人数都为1

public 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];

http:// }

for (h--; h >= 0; h--) {

parent[help[h]] = i;

}

return i;

}

六、总结

并查集算法通常用在处理一些不交集(Disjoint Sets)的合并及查询问题

力扣也有并查集的 tag 专栏:并查集

大家可以好好体会一下路径压缩的奇妙,通过不断的路径压缩,可以将我们的时间复杂度降低到O(1)

这里可能有小伙伴有疑问,为啥是O(1),你就算挂在1的下面,不也得查询两次嘛

对于时间复杂度来说,只要是常数的次数都可以认定为O(1)的时间复杂度,况且,你也可以使用HashMap来进行存储。

以上就是java并查集算法带你领略热血江湖的详细内容,更多关于java并查集算法的资料请关注我们其它相关文章!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:动态路由协议 ❀ OSPF的选路规则
下一篇:Cisco ❀ 访问控制列表-ACL(cisco交换机)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~