并查集讲解

给你一道题目:(可以看洛谷P3367

给你编号为1~n的n个点,有m次操作:

操作有两种:

1 x y

将x所在的集合和y所在的集合合并

2 x y

如果x和y在同一个集合里,输出 Y,否则输出N

给一个样例:(就是洛谷上的,我懒得造样例了

4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4

这就要用并查集求解了

首先:什么是并查集?

字面上的意思(相信看题也看得出来):并集和查集

因为集合是无序的,所以我们可以将它看成一棵树

很容易想到:当做1操作的时候,将x的父亲指向y

while(m--){
    int p,x,y;
    scanf("%d%d%d",&p,&x,&y);
    if(p==1){
        f[x]=y;//f[i]表示i的父亲
    }
}

但这有个问题 ,就是只把x的父亲指向y只能保证x以及x的孩子与y合并

我们要做的是把x节点的根节点的父亲指向y的根节点的父亲

于是我们可以写一个find()函数

int find(int x){
    if(f[x]==0)return x;//当x没有父节点,则x为根节点
    return find(f[x]);//不是的话就继续搜索它的父节点
}

但如果节点编号是从0开始的呢?

所以我们就可以把f进行一个初始化,将f[i]赋值成i

for(int i=0;i<=n;i++)f[i]=i;//n是节点个数,将f[i]全部赋值成i

 于是find()就可以这么写

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

但如果树是这样的(本人图画得不好,见谅)

 

那么遍历就会变得很慢

所以可以加一个优化,就是每次遍历的时候把x的父节点提到x的根节点上去

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

 最后,看一下最终的代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,f[10009];
int find(int x){
	if(f[x]==x)return x;
	return f[x]=find(f[x]);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1,p,x,y;i<=m;i++){
		scanf("%d%d%d",&p,&x,&y);
		int a=find(x);
		int b=find(y);
		if(p==1)f[a]=b;
		else{
			if(a==b)printf("Y\n");//如果x的根与y相同,即表明在统一集合
			else printf("N\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值