并查集(集合的合并与查找)
在一些有N个元素的集合合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。(摘自百度百科)
其中主要有三个函数:Init();初始化一个并查集,主要是根据题目需要建立一个原始的点集,因为还没有处理点与点的关系,初始化每个点的父亲结点为本身。
Find();找到并返回某个点的父亲结点,以及优化路径。
UnionSet();处理点与点的关系。如图1
(1)在处理b与a的关系时,b与a的根节点不同,而b与a又属于同一个集合,所以将b指向a,p[b]=a,即a为b的父亲结点。这时a的根节点为a,b的根节点也为a。在逻辑上将b与a连接起来 。为同一个集合里的元素。
(2)又如在处理d与b时,d的根节点为d,为b的根节点为a。而d与b又是同一个集合里的元素。所以将d指向b的根节点a。
(3)而在判断d与g是否为同一集合的元素时,只需要查找它们的根节点是否相同,由图可以看出,它们 的根节点分别为a和e,所以它们不在同一集合。
HDU 1232通畅工程
(1)题意以及输入
(2)输出
解法:
由题意可以知道,第一行给出有N个城市和M条道路。对这些道路进行处理可以发现,如果N个城市可分为k个相互联通的部分,那么只需要k-1条路将这些部分联通。所以这道题求出将这些数据用并查集函数进行处理后存在多少个根节点即可得解。
代码:
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1020;
int n,p[maxn],r[maxn],t[maxn];
void init()
{
for(int i=1; i<=n; i++)
{
p[i]=i;//初始化i点的前导点就是自己
r[i]=0;
}
return ;
}
int Find(int x)
{
if(x!=p[x])
{
int fa=p[x];
return p[x]=Find(fa);//路径优化
}
return p[x];
}
void UnionSet(int x,int y)
{
int xx=Find(x);
int yy=Find(y);
if(xx!=yy)
{
if(r[xx]>r[yy])//保证合并后的树型结构层数更低,便于提高查找效率
p[yy]=xx;//从而减少树的深度,防止树的退化为链表
else
{
p[xx]=yy;
if(r[xx]==r[yy])
r[yy]++;
}
}
return ;
}
int main()
{
int m,k,c1,c2;
while(~scanf("%d",&n))
{
if(n==0)
break;
init();
scanf("%d",&m);
for(int i=0; i<m; i++)
{
scanf("%d %d",&c1,&c2);
UnionSet(c1,c2);
}
memset(t,0,sizeof(t));
for(int i=1;i<=n;i++)
{
t[Find(i)]=1;
}
k=0;
for(int i=1;i<=n;i++)
{
if(t[i]==1)
k++;
}
printf("%d\n",k-1);
}
return 0;
}
希望对大家有所帮助!