是人都能看懂的并查集介绍

并查集

并查集是个好东东,你们一定要学会! 

序列合并需(On),并查集只需(O1)。

序列查询需(On),并查集只需(O1)。

做题想要速度快,用它!用它!用它!!!

并查集合并

刚才说了并查集的好处,接下来我们来讲一讲并查集如何把合并序列的时间复杂度从(On)化为(O1)的。

并查集的核心主要就是他有一个‘祖宗节点’,来人了就判断和他的祖宗是不是一样的,不一样就强迫他认你的祖先为祖宗(大家不要学啊!)。

 主要该如何实现呢?别急,接下来我来说亿说。

首先输入一组序列,选序列中任意一个点为祖宗(本人建议第一个点,以后方便),把序列中其他的数的祖先都设置为祖宗

然后,再输入一个序列,步骤跟上一个一模一样。

注意,每个人的祖宗开始都是自己。

想合并这两个序列的话:

1.判断祖宗是否相同,相同你还咋强迫人家认祖宗?(认着认着突然来句:我和你是亲戚呀!尴尬~

2.如果祖宗不同,就强迫b的祖宗认a的祖宗为祖宗(也就是把b序列里的数的祖宗都设置为a序列的祖宗不孝顺的子孙

然后......就没有然后了。

就这?!!

啊对,就这!

是不是很简单?

他们两个祖宗都一样了,自然就是一个序列的了。

有细心的同学可能会问了,那把b序列中的数的祖宗都设置为a序列的祖宗,时间复杂度不还是(On吗?

问得好!!!

我也不知道......

啊开个笑,我怎么会不知道呢?

b序列的祖宗(只有祖宗一个人)设置为a序列的祖宗就行了。查询b序列的时候只需要查询它的祖宗就行了。

可以这样理解。b序列的人都是大臣,平民,宰相......他们每天都是听皇上的,有事也是找皇上,他们不管皇上是谁,有事就去找皇上就对了!a序列的祖宗过来把b序列的皇帝给踹了他们照样找皇帝,只不过找的是a序列的祖宗了。

能理解了吧?

这就是并查集合并,接下来我来说亿说并查集查询

并查集查询

其实我们刚才也说了如何查询n是哪一个序列的,就是去找皇帝嘛!

比如说:

要查询平民a是哪一个皇帝手下的,就让平民a去找地主b,让地主b去找城主c,再让城主c去找大臣d,大臣d去找宰相e,宰相e再去找皇帝f,找到皇帝f了,就可以判断平民a是哪个皇帝手下的啦!

简单来说,并查集查询就是这样一个过程。

从要查询的那一个数开始,一直查询的它的祖宗。

又有同学会问了(你们怎么那么多问题嘞?):

那一直判断,时间复杂度不也是(On的吗?

问的依旧太好了,我依旧不知道。

啊我依旧开个玩笑,我依旧怎么可能会不知道呢?

咳咳,切回正题。

我们可以定义一个数组:f,每次查询到祖宗的时候,使f[(当前这个数)]=祖宗,使沿途的数的f也等于祖宗,这样我们只是第一次用到了(On),其他时候我们都是(O1​)。

这就是并查集的主要思想,接下来,上题目!!!

题目背景

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

题目描述

规定:x 和 y 是亲戚,y 和 z 是亲戚,那么 x 和 z 也是亲戚。如果 xy 是亲戚,那么 x​​​​​​​ 的亲戚都是 y 的亲戚,y 的亲戚也都是 x​​​​​​​ 的亲戚。  

输入格式

第一行:三个整数 n,m,pn,m,p<=5000),分别表示有 n 个人,m 个亲戚关系,询问 p 对亲戚关系。

以下 m 行:每行两个数 M_i,M_j1 \le M_i,M_i \le N,表示 M_i 和 M_j​ 具有亲戚关系。

接下来 p 行:每行两个数P_i,P_j,询问 P_i​ 和 P_j 是否具有亲戚关系。

输出格式

p 行,每行一个 Yes 或 No。表示第 i 个询问的答案为“具有”或“不具有”亲戚关系。

好了,首先分析一下题,这是给你两个人的关系,让你判断两人是否是一家人。

当有两个人是亲戚时,我们可以把两人的家族已知人员合并成一个家族,用到我们的并查集合并。

判断的时候,就查祖先是否是一样的,用到我们的并查集查询。

接下来,上代码!!!

#include <bits/stdc++.h>
using namespace std;
int n,m,q,f[20010];
//n,m,q是题目所需变量,f是当前这个数的祖宗 

int find(int x){
	//查询祖宗 
	if(f[x]==x) return x;
	//如果当前这个数的祖宗是这个数的话,说明这个数就是祖宗,返回它 
	return f[x]=find(f[x]);
	//否则返回当前查询的这个数(递归),顺便使当前这个数的祖宗等于真的祖宗 
}

int main(){
	scanf("%d%d%d",&n,&m,&q);
	//输入 
	for(int i=1;i<=n;i++) f[i]=i;
	//每个人开始的祖宗都是自己(逞威风) 
	while(m--){
		//循环m次,获得信息 
		int x,y;
		//定义两个变量,代表两个人 
		scanf("%d%d",&x,&y);
		//输入两个人的信息 
		f[find(x)]=find(y);
		//使当前x的祖宗等于 y的祖宗(大孝子) 
	}
	while(q--){
		//循环q次,询问信息 
		int x,y;
		//定义两个变量,代表两个人 
		scanf("%d%d",&x,&y);
		//输入两个人的信息
		x=find(x),y=find(y);
		//x等于x的祖宗,y等于y的祖宗 
		if(f[x]==f[y]) printf("Yes\n");
		//如果祖宗一样,就是一家人 
		else printf("No\n");
		//否则风马牛不相及 
	}
	return 0;
	//结束程序 
}

好了,这就是今天的内容,你们学废了吗?

.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值