并查集学习
转载 http://blog.youkuaiyun.com/StudyRush/archive/2010/02/01/5274729.aspx
如果对并查集不熟悉的话,具体的理论知识可以参考《算法导论》第21章,或者百度或者Google一下都可以,并查集有两部分很重要就是查找和合并,另外并查集一般都是用森林表示法,用链表效率低,其实用森林表示法才能够显示并查集的巨大优势。并查集的完美表现就是实现最小生成树(Kruskal+并查集),当然也不知这样一种应用,如果你熟悉并查集,那么就可以很快的写出最小生成树的Kruskal算法了。
并查集是一种树形的数据结构,用于处理一些不相交集合的合并及查询问题。常常使用中以森林表示。
并查集的主要操作:合并,查询。简单的就不介绍了,下面主要介绍并查集的两种优化。
1. 按秩合并,其思想是使包含较少节点的树的根指向包含较多节点的根。对于每一个节点,用秩表示节点高度的一个上界(rank)。
2. 路径压缩,使查找路径上的每一个节点都直接指向根节点。路径压缩并不改变节点的秩。
下面是具体实现的代码:
int father[MAXN];
int rank[MAXN];
//初始化
void Init()
{
for(int i=1;i<=MAXN;++i)
{
father[i]=i;
rank[i]=-1;
}
}
//递归的进行路径压缩
int Getfather(int x)
{
if(father[x]!=x)
father[x]=Getfather(father[x]);
return father[x];
}
//按秩合并
void Union(int x,int y)
{
x=Getfather(x);
y=Getfather(y);
if(x==y)
return;
if(rank[y]>rank[x])
father[x]=y;
else
{
if(rank[x]==rank[y])
++rank[x];
father[y]=x;
}
}
下面给出一道具体的题目
http://acm.tju.edu.cn/toj/showp2469.html
题意很容易理解,就是说人不能够跟不熟悉的住在一起,必须要熟悉的人才能够住一起,问需要多少个房间,如果对并查集熟悉的话,应该很容易想到。
参考代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=101;
int father[MAXN];
int rank[MAXN];
void Init()
{
for(int i=1;i<=MAXN;++i)
{
father[i]=i;
rank[i]=-1;
}
}
int Getfather(int x)
{
if(father[x]!=x)
father[x]=Getfather(father[x]);
return father[x];
}
void Union(int x,int y)
{
x=Getfather(x);
y=Getfather(y);
if(x==y)
return;
if(rank[y]>rank[x])
father[x]=y;
else
{
if(rank[x]==rank[y])
++rank[x];
father[y]=x;
}
}
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
int t,a,b;
int n,m;
scanf("%d",&t);
while(t--)
{
int count=0;
scanf("%d%d",&n,&m);
Init();
while(m--)
{
scanf("%d%d",&a,&b);
if(Getfather(a)!=Getfather(b))
{
Union(a,b);
count++;
}
}
printf("%d/n",n-count);
}
return 0;
}
在POJ里面还有很多练习题简单并查集的应用. (poj1182,poj1456,poj1611,poj1988,poj2524,poj2236)