并查集C++讲解与应用

目录

1.并查集概念与定义

2.并查集常见操作

2.1 find操作的实现

2.2 归并操作的实现

2.3 初始化数据的实现

3.并查集例题

3.1 P3367 【模板】并查集

题目描述

输入格式

输出格式

输入输出样例

说明/提示

3.2 P2256 一中校运会之百米跑

题目背景

题目描述

输入格式

输出格式

输入输出样例

4. 总结


1.并查集概念与定义

  并查集就是将一些不相交的集合,不断的归并、查询的一种数据结构。

举个例子:在一个群聊中,每个人不仅都是独立的个体,有的还会因为血缘,亲缘,地缘等关系而联系到一起形成一个个小群。而并查集就是根据一些条件“建立”小群并查询的过程。

在这个例子中,比如说小明是这个群里其中一个,那么根据血缘关系,他的父亲可以看作是他的“前辈”,而小明的学弟可以把小明当作“前辈”,同时小明的学弟的“前辈”也可以是小明的父亲,所以总的来说他们两个“小辈”的“前辈”都是小明的父亲。所以说经过不同的关系可一最终决定所有人的“前辈”是谁,然后可以进行查询或者计算“后辈”的数量等操作。

2.并查集常见操作

2.1 find操作的实现

find(查询“前辈”操作)是并查集中最重要的操作。下来给一段伪代码: 

#include <bits/stdc++.h>
using namespace std;
const int M=1e7+9;
int f[M];//定义一个数组来表示当前人的“前辈”是谁
int find(int k){
	if(f[k]==k)return k;//如果他的“前辈”是他自己的话直接返回他本身
	return f[k]=find(f[k]);//否则返回最更高一级的“前辈”
}
int main() {


    return 0;
}

注释中已经给好了解释也不必再多说,就是一个不断递归的过程。这个find是经过优化过的一般来说是不优化的话每一个人的“前辈”就只是上一辈,优化过后就将所有此人的“后辈”的“前辈”都化为他。

2.2 归并操作的实现

一般我自己是将归并操作直接写到main函数中,但大多数较为简洁的做法就是将其放到函数里进行实现。下面放一下函数的模板:

void join(int x,int y)                     //让y的“前辈”成为x的“前辈”
{
    int a=find(x), b=find(y);            //分别找到他们两个人的“前辈”
    if(a != b)                           //如果他们的“前辈”不为同一人
        f[a]=b;                        //就将y的“前辈”化为x的“前辈”
}

伪代码中注释已经很清楚了所以不过多解释。

2.3 初始化数据的实现

首先,在一切操作进行之前,要先对f[i]数组进行初始化使得每个人的前辈都是他自己,伪代码如下:

void init(int n)     				//初始化函数,对录入的 n个结点进行初始化 
{
    for(int i = 1 ;i < =n; i++){
        f[i] = i;     			//每个结点的上辈都是自己 
         			
    } 
}

每道题要求不同因此初始化的代码也会有所区别,此模板只是一个示例,具体题目具体编译。

3.并查集例题

3.1 P3367 【模板】并查集

题目来源:P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式

第一行包含两个整数 N,MN,M ,表示共有 NN 个元素和 MM 个操作。

接下来 MM 行,每行包含三个整数 Zi,Xi,YiZi​,Xi​,Yi​ 。

当 Zi=1Zi​=1 时,将 XiXi​ 与 YiYi​ 所在的集合合并。

当 Zi=2Zi​=2 时,输出 XiXi​ 与 YiYi​ 是否在同一集合内,是的输出 Y ;否则输出 N 。

输出格式

对于每一个 Zi=2Zi​=2 的操作,都有一行输出,每行包含一个大写字母,为 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

输出:

N
Y
N
Y

说明/提示

对于 30%30% 的数据,N≤10N≤10,M≤20M≤20。

对于 70%70% 的数据,N≤100N≤100,M≤103M≤103。

对于 100%100% 的数据,1≤N≤1041≤N≤104,1≤M≤2×1051≤M≤2×105,1≤Xi,Yi≤N1≤Xi​,Yi​≤N,Zi∈{1,2}Zi​∈{1,2}。

本题就是一道非常基础的并查集模板题具体做法就不必多说了看完代码就会了。

#include<bits/stdc++.h>
using namespace std;
const int M=10009;
int n,m,x,y,z,f[M],ans;
int find(int k){
		if(f[k]==k)return k;
		return f[k]=find(f[k]);
}

int main(){
cin>>n>>m;
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	for(int i=1;i<=m;i++){
		cin>>z>>x>>y;
		int a=find(x),b=find(y);
		if(z==1){
			f[a]=b;
		}else{
			if(a==b) cout<<"Y"<<endl;
			else cout<<"N"<<endl;
		}
	}
    return 0;
}	

3.2 P2256 一中校运会之百米跑

题目来源:P2256 一中校运会之百米跑 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目背景

在一大堆秀恩爱的 ** 之中,来不及秀恩爱的苏大学神踏着坚定(?)的步伐走向了 100100 米跑的起点。这时苏大学神发现,百米赛跑的参赛同学实在是太多了,连体育老师也忙不过来。这时体育老师发现了身为体育委员的苏大学神,便来找他帮忙。

可是苏大学神需要热身,不然跑到一半就会抽(筋)、于是他就找到了你。。。如果你帮助体育老师解决了问题,老师就会给你 55 个积分。

题目描述

假设一共有 NN(2≤N≤2×1042≤N≤2×104)个参赛选手。(尼玛全校学生都没这么多吧)

老师会告诉你这 NN 个选手的名字。

接着会告诉你 MM(1≤M≤1061≤M≤106)句话,即告诉你学生 A 与学生 B 在同一个组里。

如果学生 A 与学生 B 在同一组里,学生 B 与学生 C 也在同一组里,就说明学生 A 与学生 C 在同一组。

然后老师会问你 KK(1≤K≤1061≤K≤106)句话,即学生 X 和学生 Y 是否在同一组里。

若是则输出 Yes.,否则输出 No.

输入格式

第一行输入 NN 和 MM。

接下来 NN 行输入每一个同学的名字。

再往下 MM 行每行输入两个名字,且保证这两个名字都在上面的 NN 行中出现过,表示这两个参赛选手在同一个组里。

再来输入 KK。

接下来输入 KK 个体育老师的询问。

输出格式

对于每一个体育老师的询问,输出 Yes. 或 No.

输入输出样例

输入:

10 6
Jack
Mike
ASDA
Michel
brabrabra
HeHe
HeHE
papapa
HeY
Obama
Jack Obama
HeHe HeHE
brabrabra HeHe
Obama ASDA
papapa Obama
Obama HeHE
3
Mike Obama
HeHE Jack
papapa brabrabra

输出:

No.
Yes.
Yes.

 本题与上一题不同的是他需要将对应字符串(姓名)的下标用函数算出来,然后再进行合并和查询操作,代码如下:

#include<bits/stdc++.h>
using namespace std;
const int M=100009;
int n,m,k;
string a[M],f[M]; 
int turn(string name){
	for(int i=1;i<=n;i++){
		if(name==a[i]) return i;
	}
}
string find(string name){
	if (f[turn(name)]==name) return name;
		return f[turn(name)]=find(f[turn(name)]);
} 
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		f[i]=a[i];
	}
	for(int i=1;i<=m;i++){
		string o,b;
		cin>>o>>b;
		f[turn(find(o))]=find(b);
	}
	cin>>k;
	for(int i=1;i<=k;i++){
		string c,d;
		cin>>c>>d;
		if(find(c)==find(d)) cout<<"Yes." <<endl;
		else cout<<"No."<<endl;
	}
    return 0;
}

4. 总结

总的来说,并查集的用法,针对性较为强,主要是用于理清各个事物的等级关系。

这就是我对并查集的理解。

感谢鉴赏,留个赞再走吧( ̄▽ ̄)~■□~( ̄▽ ̄)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值