并查集

并查集

一、什么是并查集?

并查集支持查找一个元素所属的集合以外的两个元素各自所属的集合的合并等运算;

在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合“合并”,其间要反复“查找”一个元素在哪个集合中;

“并”、“查”和“集”这三个字由此而来;

在这种数据类型中,n个不同元素被分为若干组,每组是一个集合,这种集合叫分离集合,称之为并查集

这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(13秒)内计算出试题需要的结果,只能用并查集来描述。

代码:
#include <stdio.h>
int f[1000]={0},n,m,k,sum=0;


void init()         //初始化,数组存下标编号
{
    int i;
    for(i=0;i<=n;i++)
        f[i]=i;
}


int getf(int v)     //找爹的递归函数,不停找爹,直到找到祖宗为止
{
    if(f[v]==v)
        return v;
    else            //这里是压缩路径,每次函数返回的时候,顺带把路上遇到的人的“boos”改为最后找到的祖先的编号。可以提高找到祖先的速度
    {
        f[v]=getf(f[v]);
        return f[v];
    }
}


void merge(int v,int u)     //合并子集的函数
{
    int t1,t2;
    t1=getf(v);
    t2=getf(u);
    if(t1!=t2)              //判断两个结点是不是一个祖先
    {
        f[t2]=t1;           //“靠左原则”左边变成右边的boss,就是把右边的集合作为左边集合的子集合
    }                       //经过路径压缩后,将f[u]的根的值也赋值为v的祖先f[t1]
}


int main()
{
    int i,x,y;
    scanf("%d%d",&n,&m);
    init();         //初始化
    for(i=1;i<=m;i++)
    {
        scanf("%d %d",&x,&y);
        merge(x,y);         //合并
    }
    for(i=1;i<=n;i++)       //扫描有多少个独立的犯罪团伙
    {
        if(f[i]==i)
            sum++;
    }
    printf("%d\n",sum);
    return 0;
}

Description

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

Input

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

Output

P行,每行一个’Yes’’No’。表示第i个询问的答案为具有不具有亲戚关系。

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std;
int fa[50002],a,b,m,n,p;
/*
x代表例题中的人,fa[x]中所存的数代表这一集合中所有人都与一个人有亲戚关系
相当于例题中第一个集合所有的元素都与第一个元素有亲戚关系
搜索时只要找元素所指向的fa[x]=x的元素(即父元素)
然后比较两个元素的父元素是否相同就可以判断其关系
*/
void build(int qwq)
{
    for(int i=1;i<=qwq;i++)
        fa[i]=i;
        return ;
}//初始化,一开始每个点单独成集合 


int find(const int &x) 
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}//找到x的最远祖先,并且压缩路径
bool che(const int &x,const int &y)
{
    return find(x)==find(y);
}//判断x,y是不是在同一个集合里,直接判断最远祖先是不是一样的 


void mer(const int &x,const int &y)
{
    if(!che(x,y)) 
        fa[fa[x]]=fa[y];
    return ;
}
//合并x,y,我们在判断x和y是不是同一个集合里,路径压缩之后fa[x],fa[y]已经是最远祖先了,所以直接将fa[x]的父亲连接起来就好 


int main()
{
    int i;
    scanf("%d%d%d",&n,&m,&p);
    build(n);
    for(i=1;i<=m;i++)
        scanf("%d%d",&a,&b),
        mer(a,b);
    for(i=1;i<=p;i++)
    {
        scanf("%d%d",&a,&b);
        if(che(a,b))
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

我感觉这类题是一个模板 XD 

最后附带一张很有意思的图

这个图片的作者博客讲解的比我清晰,关于路径压缩方面的这个博主讲的也很清晰

图片原作者传送门 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值