定义
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。
并查集
1.初始化
把每个点所在集合初始化为其自身。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。
void init(){ for(int i=1;i<=n;i++) fa[i]=i; }
2.查找
查找元素所在的集合,即根节点。(注意,fa数组表示的不一定是父亲,大部分是祖宗,要用递归的路径压缩实现)
int find(int x){ if(fa[x]==x) return x; return find(fa[x]); }
3.合并
将两个元素所在的集合合并为一个集合。
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。
void merge(int x,int y){ x=find(x),y=find(y); if(x<y) fa[y]=x; else fa[x]=y; }
4.最后的最后
好了,不说了,直接上题目
题目
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入输出格式
输入格式:
第一行包含两个整数N、M,表示共有N个元素和M个操作。
接下来M行,每行包含三个整数Zi、Xi、Yi
当Zi=1时,将Xi与Yi所在的集合合并
当Zi=2时,输出Xi与Yi是否在同一集合内,是的话输出Y;否则话输出N
输出格式:
如上,对于每一个Zi=2的操作,都有一行输出,每行包含一个大写字母,为Y或者N
输入输出样例
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据,N<=10,M<=20;
对于70%的数据,N<=100,M<=1000;
对于100%的数据,N<=10000,M<=200000。
分析
2019年夏天,NBA自由市场开始了,无数巨星离开了自己的队伍去其他队争冠去
这道题是一道普通的并查集,代码比较短,不是特别难,首先是要合并,然后再去判断是否在一个集合,总之来说可以看做NBA的球员在不在一个队
代码
#include<iostream> using namespace std; const int N=10100; int fa[N],n,m; void init(){ for(int i=1;i<=n;i++) fa[i]=i;//每个球员都是老大 } int find(int x){//寻找老大看是否是一队 if(fa[x]==x){ return x; } return find(fa[x]); } void unite(int x,int y){//合并两名球员在一队 (例:合并伦纳德和乔治一队,选老大) x=find(x),y=find(y); if(x<y) fa[y]=x; else fa[x]=y; } int main(){ scanf("%d%d",&n,&m); init();//初始化 for(int i=1;i<=m;i++){ int u,v,z; scanf("%d%d%d",&z,&u,&v); if(z==1) unite(u,v);//合并两名球员 if(z==2){ if(find(u)==find(v)) printf("Y ");//如果在是一队的 (如果他们的老大相同,例:浓眉哥和考神的老大是詹姆斯DXX) else printf("N ");//如果不是(例:哈登老大是威少【以前的】,而西帝老大是大帝) } } return 0; }