数据结构--并查集
一、概述
1、定义:并查集是一种树型的数据结构
2、应用:用于处理不相交的集合的合并和查询。
二、操作及实现
1、查询:找到自己的老大
int find(int x)
{while(f[x]!=x)//如果自己的老大不是自己说明老大另有其人{x=f[x];//接着往下找老大}return x;//找到了老大
}
2、合并:合并成一个团伙
void join(int x,int y)
{int fx=find[x],fy=find[y];//找到两个人各自的老大if(fx!=fy)//如果两个人的老大不一样说明不是一个团伙{f[fx]=fy;//要合并成一个团伙,只能有一个老大,所以说其中一个老大要认另一个老大做新老大。}
}
3、路径压缩:(优化find函数)
如果树的深度过深,查询起来会相当耗时,所以说要减短树的深度。
可以将查询点x到根节点(也就是老大)的途径的点的父节点都设成为根节点,会大大降低查询的难度。
缺点是:只有当找到老大时,才能进行路径压缩,所以每个团体第一次的查询是没有什么优化的,之后才会生效。
int find(int x)//路径压缩
{if(f[x]==x)return x;//如果自己的老大是自己输出return f[x]=find(f[x]);//如果不是,继续找老大,并且把自己的父节点设为老大
}
三、模板和例题
1、模板
洛谷P3367
https://www.luogu.com.cn/problem/P3367#submit
https://www.luogu.com.cn/problem/P3367#submit

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int f[N],a,b,c;
int find(int k)
{//路径压缩 if(f[k]==k)return k;return f[k]=find(f[k]);
}
int main()
{int n,m;cin>>n>>m;for(int i=1;i<=n;i++){f[i]=i;//初始化老大为自己 }for(int i=1;i<=m;i++){cin>>a>>b>>c;if(a==1){f[find(b)]=find(c);//合并 c所在组赢了b所在组 }else{if(find(b)==find(c))//检查老大是否相同 {cout<<"Y"<<endl;}else{cout<<"N"<<endl;}}}return 0;
}
2、acwing 837、连通块中点的数量
https://www.acwing.com/problem/content/839/

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,f[N],cnt[N];
int find(int x)//不能用路径压缩因为那样的话就没办法统计连通快的数量了
{if (f[x] != x) f[x] = find(f[x]);return f[x];
}
void join(int x,int y)
{int fx=find(x),fy=find(y);if(fx!=fy){f[fx]=fy;}
}
int main()
{cin>>n>>m;for(int i=1;i<=n;i++){f[i]=i;cnt[i]=1;}while(m--){string flag;cin>>flag;int a,b;if(flag=="C"){cin>>a>>b;if(find(a)==find(b))continue;cnt[find(b)]+=cnt[find(a)];//先加连通块数量,否则先操作集合的话会导致重叠。而且要给新老大加,不能加错join(a,b);}else if(flag=="Q1"){cin>>a>>b;if(find(a)==find(b)){cout<<"Yes"<<endl;}else{cout<<"No"<<endl;}}else{cin>>a;cout<<cnt[find(a)]<<endl;}}return 0;
}
3、acwing 240食物链


//把食物环拆成食物链,用某节点到根节点的距离%3的余数来表示各节点之间的关系。
/*
余1:吃根节点
余2:被根节点吃
余0:同类
*/
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
int f[N],d[N];
int n,m;
int find(int x)
{if(f[x]!=x)//自己不是根节点{int t=find(f[x]);//先压缩路径使除x外的所有上级都指向根节点并记录根节点是谁d[x]+=d[f[x]];//到根节点的距离更新为x到其父节点的距离+父节点到根节点的距离f[x]=t;//再让x指向根节点}return f[x];
}
int main()
{cin>>n>>m;for(int i=1;i<=n;i++){f[i]=i;}int res=0;while(m--){int t,x,y;cin>>t>>x>>y;if(x>n||y>n){res++;continue;}int fx=find(x),fy=find(y);if(t==1)//同类{//在同一集合中if(fx==fy&&(d[x]-d[y])%3)res++;//两者%3不相等,说明不是同一类,谎话++else if(fx!=fy)//不在同一集合中,先到者为真{f[fx]=fy;//合并集合,让其中一个老大认另一个人做老大d[fx]=d[y]-d[x];//因为同类,所以(d[x]+d[fx]-d[y])%3==0}}//同上else{if(fx==fy&&(d[x]-d[y]-1)%3)res++;else if(fx!=fy){f[fx]=fy;d[fx]=d[y]+1-d[x];}}}cout<<res<<endl;return 0;
}
