并查集之简析与实现

并查集



1.并查集简介

若干点,有连接关系(可传递)的结点为一个集合,每个点都有一个父结点,可追溯到一个父结点上。

2.追溯祖结点

查找每个结点的祖结点,可用于判断两个点是否有连接关系,祖结点同便是有连接关系

    int find(int x)
    {
        int r=x;
        //r的上级如果是自己那么就是祖结点
        while (pre[r ]!=r)                  
            r=pre[r ] ;                     
        return  r ;                     
    }

3.集合建立联系

两个点建立联系会使两个集合建立联系,使两个祖结点建立联系便可以使两个集合全部建立联系,因为此时只有一个祖结点了,两个集合共一个祖结点。


void join(int x, int y)
    {
        int fx = find(x),fy = find(y);
        //祖结点不同时,x加入y
        if(fx != fy)
             pre[fx] = fy;
    }

4.路径压缩算法

为了提高查找效率,尽可能让每个结点的父结点为祖结点,便实现压缩(可替代find())


//修改查找函数,在查找中,将结点的一个个父结    点更改为祖结点
int Find(int x)
{
    int r = x;
    //找到祖结点
    while(r != pre[r])
        r = pre[r];
    int i = x,j;
    //把x的父结点的父节点等等的父节点设为祖结点
    while(pre[i]!=r)
    {
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}

5.模板题———家族(vijos)

描述若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

格式输入格式第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。

以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。

接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

输出格式P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

样例1样例输入16 5 31 21 53 45 21 31 42 35 6样例输出1YesYesNo

AC源码:

#include
#include
using namespace std;
int pre[5000], n,m,p,a,b;
int main()
{

    cin>>n>>m>>p;
    for(int i = 0;i<n;i++)
        pre[i] = i;
    for(int i=0;i<m;i++)
    {
        cin>>a>>b;
        join(a,b);
    }
    for(int i = 0;i<p;i++)
    {
        cin>>a>>b;
        printf("%s\n",Find(a)==Find(b)?"Yes":"No");
    }
    return 0;
}

你可能感兴趣的:(ACM,ACM)