并查集问题

定义
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。

1主要操作编辑

初始化

把每个点所在集合初始化为其自身。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。

查找

查找元素所在的集合,即根节点。

合并

将两个元素所在的集合合并为一个集合。
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。

2描述编辑

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚

3Input编辑

第一行:三个整数n,m,p,(n< =5000,m< =5000,p< =5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。 以下m行:每行两个数Mi,Mj,1< =Mi,Mj< =N,表示Mi和Mj具有亲戚关系。 接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

4Output编辑

P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int father[50002],a,b,m,n,p;
int find(int x){
    if (father[x]!=x) father[x]=find(father[x]);
    return father[x];
}
int main(){
    scanf("%d%d%d",&n,&m,&p);
    for (int i=1;i<=n;i++) father[i]=i;
    for (int i=1;i<=m;i++){
        scanf("%d%d",&a,&b);
        a=find(a),b=find(b);father[a]=b;
    }
    for(int i=1;i<=p;i++){
        scanf("%d%d",&a,&b);
        a=find(a);b=find(b);
        if(a==b)printf("Yes");else printf("No");
    }
    return 0;
}
 
 
 
 
 
并查集实现   POJ2524

#include<iostream>
#define MAXN 50010
using namespace std;
int father[MAXN];
int find(int x)
{
return x==father[x]?x:(find(father[x]));
}
int main()
{
int n,kase=0;
long long m;
while(cin>>n>>m &&(n||m))
{
for(int i=1;i<=n;i++)
{
father[i]=i;
}
for(int i=0;i<m;i++)
{
int a,b,c,d;
cin>>a>>b;
c=find(a);
d=find(b);
if (c != d) father[c]=d;
}
int cnt=0;
for(int i=1;i<=n;i++)
{
if(father[i]==i) cnt++;
}
cout<<"Case "<<++kase<< ": " << cnt << endl;
}
return 0;
}

 

POJ 2492并查集问题(稍微加深版)

#include<iostream>
using namespace std;
const int Max = 2005;
int n, m;
int parent[Max], opp[Max];

void make_set()
{
for(int x = 1; x <= n; x ++)
{
parent[x] = x;
opp[x] = 0;
}
}



int find_set(int x)
{
if(x != parent[x]) parent[x] = find_set(parent[x]);
return parent[x];
}



void union_set(int x, int y)
{
//cout<<"这是第M=="<<M<<"次操作,合并x和y,x=="<<x<<"y=="<<y;
x = find_set(x);
y = find_set(y);
//cout<<"他们的祖先为"<<x<<" "<<y<<endl;
if(x == y)
return;
parent[y] = x;
}



int main()
{
int t, i, x, y;
scanf("%d", &t);
for(i = 1; i <= t; i ++)
{
scanf("%d %d", &n, &m);
make_set();
bool flag = false;
while(m--)
{
scanf("%d %d", &x, &y);
if(flag)
continue;
if(find_set(x) == find_set(y))
{ // 若x,y同在一个集合,则证明有同性的可疑。
flag = true;
}
if(opp[x] == 0 && opp[y] == 0)
{
opp[x] = y;//表明y是x的异性
opp[y] = x;
}
else if(opp[x] == 0)
{
opp[x] = y;
union_set(x, opp[y]);
}
else if(opp[y] == 0)
{
opp[y] = x;
union_set(y, opp[x]);
}
else
{
union_set(x, opp[y]);
union_set(y, opp[x]);
}
}
printf("Scenario #%d:\n", i);
if(flag)
printf("Suspicious bugs found!\n\n");
else
printf("No suspicious bugs found!\n\n");
for(i=1;i<=n;i++) cout<<parent[i]<<" ";
}
return 0;
}

 

 

 

 

POJ 1703 关于并查集的种类记录问题,这种是带权并查集问题  关键在于弄清各个元素与其父节点间的关系,

第一种是已知儿子与父亲间的关系,父亲与祖父间的关系,  求儿子与祖父间的关系;

第二种是已知x与其父亲a间的关系、y与其父亲b间的关系、x与y间的关系;求a与b间的关系;

#include<iostream>
using namespace std;
#define MAXN 10010
int parent[MAXN],rank[MAXN]; //1表示异类关系;0表示同类关系

int find(int x)
{
if(x!=parent[x])
{
int fa=parent[x];
parent[x]=find(parent[x]); //更改祖先节点
rank[x]=(rank[x]+rank[fa])%2; //更改节点所属类别,节点与祖先的关系,
} // 已知儿子与父亲的关系,父亲与祖父的关系,要求儿子与祖父的关系(只有两类关系)
return parent[x];
}

void UnionSet(int x,int y)
{
int a,b;
a=find(x);
b=find(y);
if(a==b) return ;
parent[a]=b;
rank[a]=(rank[x]+rank[y]+1)%2;
}
//此处最为关键的就是求rank[a],即x的父亲a与b间的关系
//已知条件有一:x与y异类(即为1);二:x与a的关系rank[x];三:y与b的关系rank[y];
//而关系就两种,所以a与b间的关系即为上式
int main()
{
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++)
{
parent[i]=i; //记录其父节点
rank[i]=0; //记录该节点的类别
}
while(m--)
{
int a,b;
char ch;
cin>>ch>>a>>b;
if(ch=='D') UnionSet(a,b);
else
{
if(find(a)==find(b))
{
if(rank[a]==rank[b]) cout<<"In the same gang."<<endl;
else cout<<"In different gangs."<<endl;
}
else
cout<<"Not sure yet."<<endl;
}
}
}
return 0;
}

 

 

 

 

 

 

 

 

posted @ 2014-04-21 14:52  zhoudan  阅读(420)  评论(0)    收藏  举报