并查集讲解

所谓并查集,实际上可以认为是对集合的合并与查找,所以下面从集合开始讲解。
假设有集合A:1 2 3 4 5,集合B:6 7 8 9 0;假设此时有一个条件导致A中的随便一个数与B中的随便一个数在一个集合内,那么很显然AB将会合并成一个更大的集合。
接下来考虑算法:
对一个数组f[50]来说,先对其进行初始化,即:

for(int i=0;i<50;i++)
f[i]=i;

f[i]的含义为f[i]与i在一个集合内。由于此时经过初始化后,f[i]即为i本身,意思是i与自己在一个集合内。
接下来考虑如何将两个集合合并到同一个集合内。
假设有一个集合A:1 2 3 4 集合B:5 6,那么在计算机中他们应该是以下面的形式表示的(表示形式不唯一,下面只是很多情况中的一种)
在这里插入图片描述
图中的含义为f[4]=2,f[2]=1,f[3]=1;f[6]=5,之所以可以这样表示为两个集合,是因为从下往上查找时,同集合的最终结果一定是相同的,所以如果想要将集合B合并到集合A中,只需要让集合B最上面的元素连到集合A中的任意一个元素上即可,如以下图片
在这里插入图片描述
这样的话,由于6最考上的数变成了三,与其他的相同,所以A与B就等同于合并成了同一个集合。
下面给出查找的代码

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

这个是合并的代码

void unionf(int x,int y)
{
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy)			//不在同一集合内
	{
		f[fx]=fy;
	}
}

读者应该考虑到,如果在通过调用find()函数时,由于可能因为while循环执行次数过多,导致超时的问题,这种情况下,应该优化查找时的代码,下面给出为什么可以优化以及代码
还用之前的图,假设要调用find()函数,查找6的根节点,此时会在循环中查找5 3 1 的根节点,最后返回1,这也就意味着,不仅仅6的根节点是1,其他过程中的几个的根节点都是1,于是可以在求得根节点后,先不着急输出,而是再次循环一次,把路径中的根节点全部换成1,这样以后再次查找其中某个节点的根结点时,就再也不需要进入循环了。
优化代码如下:

int find(int x)
{
	int y=x;
	while(x!=f[x])
	{
		x=f[x];
	}			//循环结束后x即为根 
	while(y!=f[y])
	{
		int z=f[y];	//保存路径的下一个节点 
		f[y]=x;		//更改当前节点的根节点 
		y=z;		//回复被保存的节点 
	}
	return x;
 } 

经过这样的操作后,原图会变成这种情形
在这里插入图片描述
显然下一次再查找时所需要的时间就会大幅度降低了。
其实查找还有递归算法,而且更加简单容易理解一点,相信大家只要理解了上面的内容这个一看就懂,就不再讲述了:

int find(int x)
{
	if(x==f[x])return x;
	return f[x]=find(f[x]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值