C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++并查集

C++数据结构之并查集详解

作者:CodeRanger

这篇文章主要介绍了C++数据结构之并查集详解,并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题,并查集的思想是用一个数组表示了整片森林,需要的朋友可以参考下

一、概念:

并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。

并查集的思想是用一个数组表示了整片森林(parent),树的根节点唯一标识了一个集合

我们只要找到了某个元素的的树根,就能确定它在哪个集合里。

二、用法:

并查集用在一些有 N 个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。

这个过程看似并不复杂,但数据量极大,若用其他的数据结构来描述的话,往往在空间上过大,计算机无法承受,也无法在短时间内计算出结果,所以只能用并查集来处理。

简述一下:

1,将两个集合合并。

2,询问两个集合是否在同一集合中。

三、基本原理:

每个集合用一颗树来储存。树的根节点编号是每个集合的编号。

每个节点存储它的父节点,p[x]表示x的父节点。

四、常见问题/要处理的问题:

1,如何判断树根:if(p[x]==x)

2,如何求x的集合编号: while(p[x]!=x) x=p[x] (对照用法二)

3,如何合并两个集合:px是x的集合边界,py是y的编号集合。p[x]=y

实践题目:

一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。

现在要进行 mm 个操作,操作共有两种:

输入格式

第一行输入整数 n 和 m。

接下来 m 行,每行包含一个操作指令,指令为  M a b  或  Q a b  中的一种。

输出格式

对于每个询问指令  Q a b ,都要输出一个结果,如果 a 和 b 在同一集合内,则输出  Yes ,否则输出  No 。

每个结果占一行。

数据范围

1≤n,m≤10^5   

1≤n,m≤10^5

输入样例:

4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

输出样例:

Yes
No
Yes

先上代码:

#include<iostream>
using namespace std;
const int N = 100010;
int p[N];
int find(int x){
	if(p[x]!=x){
		p[x]=find(p[x]);
	}
	return p[x];
}
int main(){
	char op[2];
	int n,i,j,k,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		p[i]=i;
	}
	while(m--){
		int a,b;
		cin>>op>>a>>b;
		if(op[0]=='M'){
			p[find(a)]=find(b);
		}
		else{
			if(find(a)==find(b)){
				cout<<"Yes"<<endl;
			}
			else{
				cout<<"No"<<endl;
			}
		}
	}
	return 0;	
}

解读思路:

初始化:由于一开始我们得到的是几个散乱的集合,即每个数都是一个集合,所以我们初始化为 p[x]=x ,这就表示他自己就是一个集合,也可以说成他就是自己的父节点/祖宗节点。

集合合并与查找:假如我们有{1},{2}这两个单独集合,我们要把它合成一个集合,就需要把他们的祖宗节点变成一个,就是上面说到的 p[x]=p[y]=x

注:这里以谁为父节点是按你输入的顺序定的马,可以理解为随机的,例如你第一个数是1,那么之后的想要把其他数都合并在1这个数的集合里,那么他们的父节点为 p[1]=p[2]=p[3]=....p[n]=1 )之后集合就成了{1 , 2}。

那么我们的查找操作也相应完成了,既然都在一个集合里,都有一个父节点,判断两个数是否在一个集合里直接比较父节点即可,高效准确。

find()函数:这里建议自己模拟一遍。如果此时我们已经将1,2合并到一个集合中去,此时把1再执行一边find函数,根据代码:p[1]=2 -> !=1 -> p[1]=find(2) -> p[2]=2 -> return 2  结束函数。

好,我们加一问,询问某个数所在集合中的元素数量。

代码:

#include<iostream>
using namespace std;
const int N = 100010;
int p[N];
int siz[N];
int find(int x){
	if(p[x]!=x){
		p[x]=find(p[x]);
	}
	return p[x];
}
int main(){
	char op[2];
	int n,i,j,k,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		p[i]=i;
		size[i]=1;
	}
	while(m--){
		int a,b;
		cin>>op;
		if(op[1]=='1'){
			cin>>a>>b;
			if(find(a)==find(b)){
				cout<<"Yes"<<endl;
			}
			else{
				cout<<"No"<<endl;
			}
		}
		else if(op[0]=='C'){
			cin>>a>>b;
			if(find(a)==find(b)){
				continue;//注意特判,否则重复相加个数
			}
			siz[find(b)]+=siz[find(a)];
			p[find(a)]=find(b);
		}
		else{
			cin>>a;
			cout<<siz[find(a)]<<endl;
		}
	}
	return 0;	
}

思路大致相同,要找某个数所在集合中元素的个数,我们也只需找到他的父节点的标号下的元素个数即可。

我们需要开一个size[]数组存放元素个数,例如还是举上面的例子:1,2,3在同一个集合里,他们的父节点是p[2]=2,那么他们的size的值也都变成size[2]=size[3]=size[1]=3,另外这个数组也是需要维护更新的

我们看具体操作:先初始化,size[i]=1,之后相加即可。

为什么siz[]里面的参数是find(x)呢,原因还是要找到相同的父节点。

到此这篇关于C++数据结构之并查集详解的文章就介绍到这了,更多相关C++并查集内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文