数据结构之并查集

匿名 (未验证) 提交于 2019-12-02 23:34:01

什么是并查集

并查集是个神奇的树型的数据结构,多用于查看几个元素是否有关系,一般并查集构造成下图:

由此我们可以很容易地知道2和6是联通(有关系)的,因为他们他们的父亲都是1

并查集的用处

在一些有N个元素的集合应用问题中,通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,所以只能用并查集来描述。

如何构造和维护并查集

以下图举例

初始化

把每个点所在集合初始化为其自身。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。

int fa[10010] for(int i=1;i<=n;i++){ 	fa[i]=i; }//相当于自己就是自己的根 

于是乎

查找

查找元素所在的集合,即根节点。

int get(int x){//用dfs往上走即可 	if(fa[x]==x){//已经找到根 		return x; 	}else{ 		int y=get(fa[x]);//他父亲的根 		fa[x]=y;//在中间边找边连可以节约很多时间,也保证构造后掉并查集深度一定为2层 		return y;//返回他自己的根 	} } fa[get(x)]=get(y);//连 

最后通过fa就可以知道他的父亲是谁,再判断2个点的父亲是否相同即可知道是否有"关系"
把所有跟根相连的直接挂上去:
但之前所连的就会被下次覆盖掉
蓝色的表示所访问的过程,红色表示边

合并

将两个元素所在的集合合并为一个集合。
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。


这样我们就可以用o(1)的时间复杂度去判断两个点是否在同一个集合(有关系),对于一些层很深的图来说,这样无疑快了很多。

例题

朋友

在社交的过程中,通过朋友,也能认识新的朋友。在某个朋友关系图中,假定 A 和 B 是朋友,B 和 C 是朋友,那么 A 和 C 也会成为朋友。即,我们规定朋友的朋友也是朋友。
现在,已知若干对朋友关系,询问某两个人是不是朋友。
请编写一个程序来解决这个问题吧。

输入格式

第一行:三个整数 ,分别表示有 个人, 个朋友关 系,询问 对朋友关系。
接下来 行:每行两个数 , ,表示 和 具有朋友关系。
接下来 行:每行两个数,询问两人是否为朋友。

输出格式

输出共 行,每行一个 “Yes” 或 “No” 。表示第 个询问的答案为是否朋友。

样例输入

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

样例输出

Yes Yes No

对于这道题来,只需要每次输入两个数就连边并维护并查集,最后看某两个数的根节点是否相同即可

代码:

#include<bits/stdc++.h> using namespace std; int n,m,p; int fa[10010]; void init(){ 	for(int i=1;i<=n;i++){ 		fa[i]=i; 	} } int get(int x){ 	if(fa[x]==x){ 		return x; 	}else{ 		return get(fa[x]); 	} } int main(){ 	cin>>n>>m>>p; 	init(); 	for(int i=1;i<=m;i++){ 		int x,y; 		cin>>x>>y; 		fa[get(x)]=get(y); 	} 	for(int i=1;i<=p;i++){ 		int x,y; 		cin>>x>>y; 		if(get(x)==get(y)){ 			cout<<"Yes"; 		}else{ 			cout<<"No"; 		} 		cout<<" "; 	} 	return 0; } 

下期――带权的并查集
标准结尾:
盗图者爆零

文章来源: https://blog.csdn.net/The_yja_of_Oier/article/details/90317044
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!