并查集模板(寻找祖先用递归实现):
1 #include<stdio.h>
2 #include<string.h>
3
4 const int maxn=1e5+5;
5 int fa[maxn];
6
7 void init(int n){ //初始化函数
8 for(int i=0;i<=n;i++)fa[i]=i;
9 }
10
11 int find(int x){ //寻找祖宗
12 return fa[x]==x?x:fa[x]=find(fa[x]);
13 }
14
15 void unio(int x,int y){ //合并两点
16 int dx=find(x),dy=find(y);
17 if(dx!=dy)fa[dx]=dy;
18 }
并查集模板(寻找祖先用循环实现,速度上并没有发现显著提高,但学长说可以避免卡栈溢出,合并过程直接在主函数内实现):
1 #include<stdio.h>
2 #include<string.h>
3
4 const int maxn=1e5+5;
5 int fa[maxn];
6
7 void init(int n){
8 for(int i=0;i<=n;i++)fa[i]=i;
9 }
10
11 int find(int x){
12 int r=x,t;
13 while(r!=fa[r])r=fa[r]; //先循环一遍寻找祖先
14 while(x!=r){ //再循环一遍更新父亲结点
15 t=fa[x];
16 fa[x]=r;
17 x=t;
18 }
19 return r;
20 }
21
22 int x=find(a),y=find(b); //主函数中完全可以直接实现合并
23 if(x!=y)fa[x]=y;
带权并查集(仅记录集合元素个数):
1 #include<stdio.h>
2 #include<string.h>
3
4 const int maxn=1e5+5;
5 int fa[maxn],num[maxn];
6
7 void init(int n){
8 for(int i=0;i<=n;i++){fa[i]=i;num[i]=1;} //初始化每个集合元素个数为1
9 }
10
11 int find(int x){
12 int r=x,t;
13 while(fa[r]!=r)r=fa[r];
14 while(x!=r){
15 t=fa[x];
16 fa[x]=r;
17 x=t;
18 }
19 return r;
20 }
21
22 int x=find(a),y=find(b);
23 if(x!=y){
24 fa[x]=y;
25 num[y]+=num[x]; //合并集合时在祖先结点上改合并后的集合元素个数
26 }
种类并查集(本质也是带权并查集,但是是记录元素的性质的,权值一般表示与父亲结点的关系):
1 #include<stdio.h>
2 #include<string.h>
3
4 const int maxn=1e5+5;
5 int fa[maxn],num[maxn];
6
7 void init(int n){
8 for(int i=0;i<=n;i++){
9 fa[i]=i;
10 num[i]=0; //此处表示和父亲节点的大小差距,初始化0
11 }
12 }
13
14 int find(int x){ //这里压缩路径并更新结点权值的思想是先循环加每个结点到父节点的权值,再次循环时每次减去之前更新完的权值再更新
15 int r=x,t1,t2,c=0;
16 while(r!=fa[r]){
17 c+=num[r];
18 r=fa[r];
19 }
20 while(x!=r){
21 t1=fa[x];
22 t2=c-num[x];
23 num[x]=c;
24 fa[x]=r;
25 c=t2;
26 x=t1;
27 }
28 return r;
29 }
30
31 int x=find(a),y=find(b);
32 if(x!=y){ //合并时的操作
33 fa[x]=y;
34 num[x]=num[b]+v-num[a];
35 }