专题2:并查集

Part 1 并查集的概念

见名知义,并查集包含并,查,集三个模块

由一个例题来看(出自洛谷P1551)

emmmm 找亲戚,确实是一个很麻烦的问题,我一想到这个问题就

啪!现在是在做程序题,又不是让你真的找亲戚

先用模拟来看

操作/集合(1)(2)(3)(4)(5)(6)
1(1,2)(3)(4)(5)(6) 
2(1,2,5)(3)(4)(6)  
3(1,2,5)(3,4)(6)   
4(1,2,5)(3,4)(6)   
5(1,2,3,4,5)(6)    

这样一次一次操作似乎的确可以,可是这怎么在电脑上实现呢?

我们的电脑中确实无法建立一个时刻可以更改的集合,如果用数组模拟,每次合并两个元素就太麻烦了,每次都要查询后合并。时间复杂度未免太大

这样,我们就想到了图(树)

让我们再来一遍模拟,再一次观看一个家庭是如何产生的,6可能是最可怜的

               

                            图1                  

 

                                图2

         

                             图3                       

 

                                图4

                       

                                    图5       

  

                                   图6

这样,我们就建立了各个家人(节点)之间的动态关系(可以随时更新),而这,就是并查集的精髓

并,也就是合并集合或元素

查,也就是查询两个元素是否在同一个集合中

集,也就是集合

Part 2 并查集的基本操作

并:unionn

合并两个集合或两个元素或是一个集合和一个元素,就像上面的图5到图6,图1到图2,图2到图3,使它们之间形成一种有机的联系,让我们在查询某一个数时,能够找到和他在同一个集合的数

定义f数组,f [ i ] = j表示 i 的父节点为 j ,这样就能一层一层找上去

void unionn(int x,int y)
{
	int r1=find(x);
    int r2=find(y);
	if(r1!=r2)
    f[r2]=r1;
}

按照这样,我们再来一遍模拟

第 j 次操作后 f [ i ]/i123456
1113456
2113416
3113316
4113316
5111316

现在似乎出现了一个问题,1和4明明在同一个集合内,他们的 f [ i ] 值并不一样,那我们怎么判断两个元素是否在同一个集合内呢?接下来,就要看查的神奇操作了

查:find

查询两个元素是否在同一个集合,上面的并操作把两个元素的父节点相连,因此查找两个元素时,只要查找他们的根节点即可

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

此处使用递归查找,可以在之后有更好的优化

我们继续以上的模拟,解决之前的问题,可以发现,find(4)=find(3)=1    find(3)=1 所以他们仍然属于同一集合,只不过在我们find 节点4时间接经过了节点3,最终找到他们的最终父节点(也就是根节点)是相同的,所以3和4属于同一集合

Part 3 并查集的路径优化

可能有人感觉到,这样一个一个进行查询,寻找他们的父节点,再向上找,未免也太麻烦了。当一条单链很长时,这种操作肯定会TLE

由此,我们有一种优化方式——路径优化,这种做法可以在将两个集合合并时,将集合中所有的节点直接连接到根节点上

int find(int x)
{
    if(f[x]!=x)f[x]=find(f[x]);
    return f[x];
}

看起来这样并没有什么改变,接下来举一个特殊例子,大家就能感受到了

如左图,当我们要找2和305是否在同一个集合时,需要向上查找300+次,但到了右图,我们只需要搜索2次即可,这样就大大增加了时间效率,这种做法大概就不会TLE了

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值