并查集的介绍 |
并查集的实现 |
const int N = 10000;
int f[N];
for(int i = 1;i <= n;i++)
{
f[i] = i;
}
(2)查找,即找x结点的根结点,如果x的根节点是它本身,则直接返回,否则递归的去找它的父结点。
int Find(int x)
{
if(x == f[x]) //如果x就是根节点
{
return x;
}
return Find(f[x]); //f[x]是x的父结点,向上递归查找
}
(3)合并,已知x和y有关系,则将x和y合并到一个父结点之上,比如可以将y合并到x上,此时x就是y的父结点。
并查集的例题 |
题目选自:
题目背景
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
题目描述
规定: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,表示Mi和Mj具有亲戚关系。
接下来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
输出 #1
Yes
Yes
No
#include<iostream>
using namespace std;
const int N = 10000;
int f[N];
int Find(int x)
{
if(x == f[x]) //如果x就是根节点
{
return x;
}
return Find(f[x]);
}
void Union(int x,int y)
{
x = Find(x);
y = Find(y);
if(x != y){
f[y] = x;
}
}
int main()
{
int n,m,p,x,y;
cin>>n>>m>>p;
for(int i=1;i<=n;i++)
{
f[i] = i;
}
for(int i=0;i<m;i++){
cin>>x>>y;
Union(x,y);
}
for(int i=0;i<p;i++){
cin>>x>>y;
if(Find(x)==Find(y)){
cout<<"Yes"<<endl;
}else{
cout<<"No"<<endl;
}
}
return 0;
}
并查集的优化 |
上面的并查集的复杂度在有些极端的情况下会很慢。比如树的结构正好是一条链,那么最坏情况下,查询的复杂度会达到O(n) 。路径压缩,只关注每个结点的父结点,在查询的时候,把查询路径上的所有结点的f[i]都赋值成为根结点。
int Find(int x)
{
if(x == f[x])
{
return x;
}
return f[x] = Find(f[x]); //返回父结点的根节点,并让父结点为根结点
}