说明
并查集是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很多。一般采取树形结构来存储并查集。在一些应用问题中,我们需要划分n个不同的元素成若干组,每一组的元素构成一个集合。这种问题的一个解决办法是,在开始时,让每个元素自成一个单元素集合,然后按一定顺序将属于同一组的元素所在的集合合并。其间要反复用到查找一个元素在哪一个集合的运算。
主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
合并(Union):把两个不相交的集合合并为一个集合。
查询(Find):查询两个元素是否在同一个集合中。
例题
朋友圈
有n个人,编号1-n。
现在有一个舞会,在舞会上,大家会相互介绍自己的朋友。
即: 如果a认识b,b认识c。那么在舞会上,a就会通过b认识到c。
现在,给出m个关系
每个关系描述:
a b
表示 编号为a和编号为b的人是朋友关系。
格式
输入格式
输入n和m
接下来m行,每行为a b
输出格式
最后问,会有多少个朋友圈。
思路:先是有n个人标号1~n,先假定每个人都是朋友圈的老大,用pre数组初始化,pre里的值就可以了解谁为当前朋友圈的老大,当输入其中俩个人是朋友时,就调用f函数找到它们的老大是谁,如果老大不一样那就把任意一个人加入这个朋友圈,他的老大也要改变,这样他们就是在一个朋友圈内。最后在一个循环内看有几个pre数组里的值是它自己,就代表有一个朋友圈,把它累计下来,最后输出就是有几个朋友圈了。
代码实现如下
#include<stdio.h>
#include<string.h>
int pre[900000];
int f(int x)
{
int r=x;
while(r!=pre[r])//找到自己的老大
r=pre[r];
int q=x,w;
while(pre[q]!=r)//路径压缩,自行去了解
{
w=pre[q];
pre[q]=r;
q=w;
}
return r;
}
int join(int a,int b)
{
int fa=f(a),fb=f(b);
if(fa!=fb)
{
pre[fb]=fa;//更改自己的老大
}
}
main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(pre,0,sizeof(pre));
int sum=0;
int i,j,k,a,b;
for(i=1;i<=n;i++)
pre[i]=i;
for(i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
join(a,b);
}
for(i=1;i<=n;i++)
if(pre[i]==i) sum++;//看看有几个朋友圈的老大是自己,就有几个朋友圈
printf("%d\n",sum);
}
}