并查集主要包括初始化、寻根以及合并三个操作。
例如a、b、c、d、e,初始化他们的祖先为本身。
寻根操作:
int find_root(int x){//非路径压缩 return x==s[x]?x:finde_root(s[x]); }
上述寻根操作,没有对路径进行压缩。具体表现为如果a的祖先是b,b的祖先是c,c的祖先是d。那么在对a进行寻根的时候会查找三次,即树太高了
int find_root(int x){//路径压缩 if(x!=s[x]) s[x]=find_root(s[x]); return s[x]; }
给出非递归版本
int find_root(int x){ int r=x; while(r!=s[r]) r=s[r];//找到x最大的祖先 int i=x; int j; while(i!=r){ j=s[i];//先把i此时的祖先存好 s[i]=r;//然后把此时的祖先的祖先设为最终的祖先即r i=j;//i变为上面的一个祖先 } return r; }
使用了路径压缩之后,相当于a,b,c的祖先直接就是d,那么只需要查询一次即可,使用路径压缩的时候,我们每次寻根的时候都需要调用find_root,这样是为了路径能一直更新。
合并操作:
void merge_set(int x,int y){ x=find_root(x); y=find_root(y); if(x!=y){ s[x]=y; } }
合并之前我们调用了find_root函数找到x和y的祖先,按理来说既然已经使用了路径压缩,那么直接使用s[x]和s[y]也是可以达到找到祖先的效果,但是要考虑到一点,如果此时路径还没压缩,那么直接找找不到祖先节点。
例子:洛谷P3367
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 �,�N,M ,表示共有 �N 个元素和 �M 个操作。
接下来 �M 行,每行包含三个整数 ��,��,��Zi,Xi,Yi 。
当 ��=1Zi=1 时,将 ��Xi 与 ��Yi 所在的集合合并。
当 ��=2Zi=2 时,输出 ��Xi 与 ��Yi 是否在同一集合内,是的输出 Y
;否则输出 N
。
输出格式
对于每一个 ��=2Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y
或者 N
。
#include<bits/stdc++.h> using namespace std; const int MAXN=10004; int s[MAXN]; int find_root(int x){ if(x!=s[x]) s[x]=find_root(s[x]); return s[x]; } void merge_set(int x,int y){ x=find_root(x); y=find_root(y); if(x!=y){ s[x]=y; } } void isOneSet(int x,int y){ x=find_root(x); y=find_root(y); if(s[x]!=s[y]){ cout<<'N'<<endl; }else cout<<'Y'<<endl; } void init_set(int N){ for(int i=0;i<N;i++){ s[i]=i; } } int main() { int N,M,x,y,z; cin>>N>>M; init_set(N); for(int i=0;i<M;i++){ cin>>z>>x>>y; if(z==1){ merge_set(x,y); }else{ isOneSet(x,y); } } return 0; }
标签:路径,return,int,查集,祖先,root,find From: https://www.cnblogs.com/hailanben/p/17249525.html