并查集的应用:
1.以树作为节点的组织结构,结构的形态很是否采取优化策略有很大关系,未进行优化的树结构可能会是“畸形”树(严重不平衡,头重脚轻,退化成链表等),按尺寸(正规说法叫做秩,后文全部用秩来表示)进行平衡,同时辅以路径压缩后,树结构会高度扁平化。
2.虽然组织结构比较复杂,数据表示方式却十分简洁,主要采用数组作为其底层数据结构。一般会使用两个数组(parent-link array and size array),分别用来保存当前节点的父亲节点以及当前节点所代表子树的秩。第一个数组(parent-link array)无论是否优化,都需要使用,而第二个数组(size array),在不需要按秩合并优化或者不需要保存子树的秩时,可以不使用。根据应用的不同,可能需要第三个数组来保存其它相关信息,比如HDU-3635中提到的“转移次数”。
3.主要操作包括两部分,union以及find。union负责对两颗树进行合并,合并的过程中可以根据具体应用的性质选择是否按秩优化。需要注意的是,执行合并操作之前,需要检查待合并的两个节点是否已经存在于同一颗树中,如果两个节点已经在一棵树中了,就没有合并的必要了。这是通过比较两个节点所在树的根节点来实现的,而寻找根节点的功能,自然是由find来完成了。f
p[N],在此表示每个节点的父节点;及在进行find之后,该节点对应的祖先节点。
初始化:每个节点都是自己父节点,p[i]=i;
代码:
#include <iostream>
using namespace std;
const int N=100010;
int p[N];
int n,m;
//查找祖先节点(祖先节点p[x]==x)
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) p[i]=i;
while(m--)
{
char op;
int x,y;
cin>>op>>x>>y;
int px=find(x),py=find(y);
if(op=='M') //合并俩个集合
{
if(px!=py)
{
p[px]=py; //将
}
}
else
{
if(px==py) puts("Yes");
else
puts("No");
}
}
return 0;
}
例题二:连通块中点的数量
给定一个包含n个点(编号为1~n)的无向图,初始时图中没有边。
现在要进行m个操作,操作共有三种:
- “C a b”,在点a和点b之间连一条边,a和b可能相等;
- “Q1 a b”,询问点a和点b是否在同一个连通块中,a和b可能相等;
- “Q2 a”,询问点a所在连通块中点的数量;
输入格式
第一行输入整数n和m。
接下来m行,每行包含一个操作指令,指令为“C a b”,“Q1 a b”或“Q2 a”中的一种。
输出格式
对于每个询问指令”Q1 a b”,如果a和b在同一个连通块中,则输出“Yes”,否则输出“No”。
对于每个询问指令“Q2 a”,输出一个整数表示点a所在连通块中点的数量
每个结果占一行。
数据范围
1≤n,m≤1051≤n,m≤105
输入样例:
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
输出样例:
Yes
2
3
思路:
每个连通块就是一颗树,可以用并查集来做。需要用一个size数组来记录当前联通块中的点的数量,保证点的数量只存在整颗数的根节点处。这样加的时候才不会重复累加。
代码:
#include <iostream>
using namespace std;
const int N=100010;
int p[N],sizes[N];
int n,m;
//查找树根
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
//初始化
for(int i=1;i<=n;i++)
{
p[i]=i;
sizes[i]=1;
}
while(m--)
{
char op[2];
int a,b;
cin>>op;
if(op[0]=='C')
{
cin>>a>>b;
int pa=find(a),pb=find(b);
if(pa!=pb)
{
p[pa]=pb;
sizes[pb]+=sizes[pa]; //将a的所有节点累加到b的根节点上
}
}
else if(op[1]=='1')
{
cin>>a>>b;
if(find(a)==find(b)) puts("Yes");
else puts("No");
}
else
{
cin>>a;
cout<<sizes[find(a)]<<endl;
}
}
return 0;
}