并查集
并查集是一种树结构,可以把它看成是若干个集合,相同集合的点有相同的根节点;每一个点都有自己的代表元素,初始化时所有点的代表元素都是它本身,最终同一个集合里的点把它的代表元素递归上去,就可以找到它们公共的根;
并查集的几种基本操作:
1、合并
2、查找
3、路径压缩
合并
合并两个点,首先要找到这两个点的代表元素;
如果f[i]代表i的代表元素,n1,n2分别是查找到的两个值,那么f[n1]=n2,或f[n2]=n1;这样就完成了合并操作,因为两个点的代表元素相同了;
查找和路径压缩
普通的查找会使并查集的时间复杂度高得难以承受,路径压缩会很好地解决这种情况;所谓路径压缩就是指在查找的时候对于每一次查找都更新那个点的代表元素,最终使所有点的代表元素都指向一个点,而不是只有一个公共根;换句话说,就是让并查集这棵树深度达到2,而不是一条链。(查找就是递归的过程)
我们通过一个例子和代码来理解:
【codevs1073/tyvj1251】家族
描述
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
输入格式
第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
输出格式
P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
测试样例1
输入
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
输出
Yes
Yes
No
并查集裸题
【代码】
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,p,i,f[5005],x,y;
int find(int x)//查找+路径压缩
{
if (f[x]==x) return f[x];
f[x]=find(f[x]);
return f[x];
}
void merge(int x,int y)//合并操作
{
int f1=find(x);
int f2=find(y);
f[f1]=f2;
return;
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
for (i=1;i<=n;++i)
f[i]=i;
for (i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
merge(x,y);
}
for (i=1;i<=p;++i)
{
scanf("%d%d",&x,&y);
if (find(x)==find(y))
printf("Yes\n");
else
printf("No\n");
}
return 0;
}