所谓并查集,就是把一些有关的东西合并起来,方便查找。它的作用是快速判断两个数是否同一集合,与快速将两个集合并成一个集合并求出一些节点之间的关系。
并查集的主要操作有下面三种:
1、初始化:把每个点所在集合初始化为其自身;
2、查找:查找元素所在的集合,即根节点;
3、合并:将两个元素所在的集合合并为一个集合。
在并查集中,每个节点记录着它的父亲(或者是它的老祖宗),这样就可以方便的找到它这个集合的领头人。集合的领头人有一个特点,它的父亲是它自己,因为是它创造了它。如果某个特别的时刻,它发现它居然还有兄弟,它会认比较年长的作它的父亲,它的孩子们就认年长的兄弟作它们的老祖宗。这样这个家族日益庞大,它们有一个共同特点,有一个相同的老祖宗。(并查集原理)
为了让我能最快的找到我的老祖宗,我向父亲询问我的祖籍,直接把我的祖宗当作父亲记住。以后如果有孩子问我,谁是它的祖宗?我就把我的父亲告诉它,减少了访问真父亲的过程。据此原理,在把x加入y集合的时候,只要让x认y作父亲就可以了,上层变了,下层在搜索时,自然也会记住y。(路径压缩)
例题:(来源:caioj 1092)
n个人,m条关系(x,y),表示x和y是同一家族的。求最多可能有多少个家族。
模版代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int fa[100010];
int find_fa(int x)
{
if(fa[x]!=x) return fa[x]=find_fa(fa[x]);//如果我不是家族的创造者
return x;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) fa[i]=i;//初始化:每个人都是自己集合的标志
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int fx=find_fa(x),fy=find_fa(y);//查找老祖宗
if(fx!=fy)
{
if(fx<fy) fa[fy]=fx;//合并集合:我的父亲认你当父亲当父亲,那我的爷爷就是你的父亲(我们是一个家族的)
else fa[fx]=fy;
//只管更新父亲的关系就好了,孩子们找祖宗时,自然会向父亲询问最新情况
}
}
int cnt=0;
for(int i=1;i<=n;i++)
{
if(find_fa(i)==i) cnt++;//每有一个创造者,就多了一个家族
//为什么不能改成 if(fa[i]==i) cnt++; ? (看回28行)因为fa中记录的不一定是最新的情况,必须先获得最新信息后再作判断
}
printf("%d",cnt);
return 0;
}
推荐:《并查集—应用》http://blog.csdn.net/a_bright_ch/article/details/77162197