并查集

0. 前置知识

哈哈,简单到爆,没有。

1.引入

并查集是一种快到爆炸的集合算法,可以进行两项基本操作:合并两个集合(并)、查询两个参数是否在一个集合内(查)。这也是它名字的由来。

2.速度

他有多快呢?
O ( ∗ l o g n ) O(*log n) O(logn)
∗ l o g *log log有多可怕:

n n n ∗ l o g n *log n logn
( 1 , 2 ] (1,2] (1,2] 1 1 1
( 2 , 4 ] (2,4] (2,4] 2 2 2
( 4 , 16 ] (4,16] (4,16] 3 3 3
( 16 , 65536 ] (16,65536] (16,65536] 4 4 4
( 65536 , 2 65536 ] (65536,2^{65536}] (65536,265536] 5 5 5

所以n是 2 65536 2^{65536} 265536的时候复杂度还只有5。
2 65536 2^{65536} 265536有多大,我把他拷进来的时候整个电脑卡死了。我不得不强制重启,然后重新写一遍这段。他有19729位。想通了吧?

3.如何实现

这么高端的算法,是怎么实现的呢?
其实

它的本质就是一个数组 f [ ] f[] f[],和一个函数 g e t f ( ) getf() getf()
f [ ] f[] f[]存的实际上就是几棵树。
f [ i ] f[i] f[i]就是 i i i的父亲。
g e t f ( i ) getf(i) getf(i)做的操作就是递归顺着 f [ i ] f[i] f[i] i i i所在的树的根。
g e t f ( ) getf() getf()代码:

int getf(int x){
	if(f[x]==x)return x;
	else return getf(f[x]);
}

. . . ... ...

那这个算法就很低端了 . . . ... ...

那还讲个鬼啊 . . . ... ...

所以

4.超级优化

我们首先随机造出一些操作:

10
HHBB 1 5
HHBB 5 2
HHBB 1 3
CCXX 2 3
HHBB 3 4
HHBB 6 7
CCXX 1 7
HHBB 7 8
HHBB 8 9
HHBB 1 9
CCXX 7 4

其中,HHBB代表合并,CCXX代表查询。
如果按照刚才所的算法,那么在第一次查询之前,就会出来这样的森林:在这里插入图片描述
到最后,就形成了这样一个繁杂的森林,要找到一个点的根,就需要走很长一段路。这就拉长了时间。
在这里插入图片描述
为了缩减时间,超级优化就出现了:路径压缩。
路径压缩其实也很简单:在 g e t f ( ) getf() getf()查找根节点的同时,把自己也链接到根节点上,使得树的深度不超过2。
代码:

int getf(int x){
	if(f[x]==x)return x;
	else return f[x]=getf(f[x]);
}

发现没有,和之前的代码相比,只改了一个地方,就让时间大大压缩。
这时我们在模拟一下。
第一次合并:在这里插入图片描述
第二次合并时,首先寻找2,5两个节点的根节点,2的根就是2,5的根是1,于是直接把2链接到1上。
在这里插入图片描述
第三次,第四次合并把3链接到了1上,然后把4顺着3也链接到了1上,第五次连接了6和7。
在这里插入图片描述
第七次第八次链接成了 9 → 8 → 7 → 6 9\rightarrow8\rightarrow7\rightarrow6 9876一长串,然后经过路径压缩都链接到6上了。
在这里插入图片描述
最后一次,把9和1链接起来了,这时深度又超过了2,一下还压缩不下去,不过没关系,查询的时候就会把它压缩的。在这里插入图片描述
比如查询7和4的时候就会分别寻找7和4的根节点,一路递归找上去的时候就直接把路径压缩好了,除了8还链接在6上,其他全部链接到1上了。
在这里插入图片描述
多么有趣啊!

5.代码

自己想去吧,核心代码和思路都给出来了。
有一个巨大的坑,就是 f [ i ] f[i] f[i]要预设成 i i i,不然会爆炸。
加油!克服恐惧的最好办法就是面对恐忄快去写吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值