并查集

这是我再一次对并查集的理解,这次比上次理解的更加深刻了
这是上一次的**并查集**
题目是这样

输入
输入的第一行包含两个用空格隔开的正整数n和k,其中n不超过100,k不超过n-1。
之后的k行中,每行包含两个用空格隔开的正整数x和y,表示将x元素所在的集合和y元素所在的集合合并至同一个集合。保证x和y均在1至n之间。
最后一行中,包含两个正整数,表示需要判断是否在同一个集合的元素编号。
输出
共一行,包含字符串“YES”或“NO”,“YES”表示需判断的元素在同一个集合中,“NO”表示不在同一个集合中。请注意不需要输出引号,且行尾输出换行。
样例输入
5 2
1 3
2 3
1 2
样例输出
YES

主要的思想呢?是查找每个点的父结点,如果父结点相同,那就不需要合并了,如果父结点相同,那么就需要合并了,
比如1的父结点是2,2的父结点是3,3是自己的父结点,
这个时候我们想合并 1和5的两个子集,1的最终父结点是2,而5的最终父结点是5,明显不同,所以就需要合并了,
先不谈路径压缩,

int find_root(int x)
{
	while(vis[x] != -1){
		x = vis[x];
	}
	return x;
}

这样找父结点的就行了,但这样会有一个问题,1找2,2找3,要执行两次,这种数据小的看不出来什么变化,但万一数据很大呢?
我们就可以把1的父结点直接变成3,这样一下子就找到了,这就是路径压缩,而上面的那段程序不可以,
我们可以试试这样

int find_root(int x)
{
	if(vis[x] != -1) return vis[x] = find_root(vis[x]);//这里最关键
	else return x; 
}

大家可以自行比较两个程序的不同之处,**这里为什么要用递归呢?**因为父结点是最后被找到的,

代码如下 :

#include <stdio.h>
int vis[105];
//vis[i] = a
//i的根结点是a 
/*
int find_root(int x)
{
	while(vis[x] != -1){
		x = vis[x];
	}
	return x;
}
*/
int find_root(int x)
{
	if(vis[x] != -1) return vis[x] = find_root(vis[x]);
	else return x; 
}
void merge(int x,int y)
{
	int root_x = find_root(x);
	int root_y = find_root(y);
	if(root_x == root_y){
		return;
	}else {
		//merge 
		vis[x] = y;
	}
}
int main()
{
	int n,k;
	scanf("%d %d",&n,&k);
	for(int i = 1;i <= n;i++){
		vis[i] = -1;
	}
	for(int i = 1;i <= k;i++){
		int x,y;
		scanf("%d %d",&x,&y);
		merge(x,y);
	}
	int x,y;
	scanf("%d %d",&x,&y);
	if(find_root(x) == find_root(y)){
		printf("YES\n");
	}else {
		printf("NO\n");
	}
	/*
	for(int i = 1;i <= n;i++){
		printf("%d ",vis[i]);
	}
	*/
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值