并查集初学
主要操作
初始化
把每个点所在集合初始化为其自身。 通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。查找
查找元素所在的集合,即根节点。合并
将两个元素所在的集合合并为一个集合。通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。
举例
初始状态
(2,4) {2,4} (5,7) {2,4} {5,7} (1,3) {1,3} {2,4} {5,7} (8,9) {1,3} {2,4} {5,7} {8,9} (1,2) {1,2,3,4} {5,7} {8,9} (5,6) {1,2,3,4} {5,6,7} {8,9} (2,3) {1,2,3,4} {5,6,7} {8,9}最后我们得到4个集合{1,2,3,4}, {5,6,7}, {8,9}, ,于是判断两个人是否亲戚的问题就变成判断两个数是否在同一个集合中的问题。
ACM例题:
【题目链接】http://poj.org/problem?id=2524
【题目大意】
给定一个n,m,n(<=50000)表示有n个人,标号从1到n,然后是m行关系: x y,表示x和y是同宗教的,问根据这m个关系最终确定宗教个数的最大值是多少。例如n=10, m=1, 然后下一行是3 5,那么3和5是同一个宗教的,而其余的关系未给出,那么个数最大就是9.
【代码】
View Code
1.
#include<stdio.h>
int f[50010];
int Find(int x){
if(x==f[x]) return x;
else return f[x]= Find(f[x]);
}
int main()
{
int n,m,x,y,i,TT=0;
while(scanf("%d%d",&n,&m),(n+m))
{
for(i=1;i<=n;i++) f[i]=i;
while(m--){
scanf("%d%d",&x,&y);
int fx= Find(x),fy= Find(y);
if(fx!=fy) f[fy]=fx;
}
int count=0;
for(i=1;i<=n;i++)if(f[i]==i) count++;
printf("Case %d: %d\n",++TT,count);
}
}
2.
#include<stdio.h>
#define N 50005
int f[N];
bool v[N];
int find(int pos)
{
if(f[pos]==-1)return pos;
return f[pos]=find(f[pos]);
}
int un(int a,int b)
{
int fa=find(a);
int fb=find(b);
if(fa==fb)return 0;
f[fa]=fb;return 1;
}
int main()
{
int n,m,i,a,b,ca=1;
while(scanf("%d%d",&n,&m), n||m)
{
//初始化
for(i=1;i<=n;i++)
{
f[i]=-1;
v[i]=0;
}
//输入,合并
for(i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
un(a,b);
}
//标记宗教
int temp;
for(i=1;i<=n;i++)
{
temp=find(i);
v[temp]=1;
}
//统计
int add=0;
for(i=1;i<=n;i++)
{
if(v[i]==1)
add++;
}
printf("Case %d: ",ca++);
printf("%d\n",add);
}
}