并查集

并查集的数据结构记录了一组分离的动态集合 S={S1,S2,S3….,Sk}。每个集合通过一个“代表”加以识别,代表即该集合的一个元素。
并查集的基本操作:
(1)Make_Set(x):建立一个新的集合,且其中仅有的元素为x。各集合是分离的,x没有在其他集合中出现过。
(2)Find_Set(x):获得x所在集合的“代表”。
(3)Union(x,y):将元素x所在集合Sx和元素y所在集合Sy合并为一个新的集合。新合并的集合的”代表”是Sx的代表或者Sy的代表。

并查集本身不具有结构,必须借助一定的数据结构加以实现。并查集的数据结构实现方法比较多,一般用的比较多的有数组实现、链表实现和树实现。下面主要介绍树实现方法。
树实现:用有根树来表示集合,树中的每个节点代表集合中的一个元素。树的根是该集合的“代表”。每个节点有一个指针指向其父亲节点,根节点的父节点指向自身。一个集合代表一个树,多个集合形成一个森林。

主要讲一下集合树的合并:显而易见,在较低的树中查找根节点比较快,所以,假如有两颗分离的集合数A和B,深度分别为ha和hb,若ha>hb,则应将B树作为A树的子树,新树的深度为ha。若ha < hb,则将A树作为B树的子树,新树的深度为hb。若ha=hb,则任意选一棵树作为另一颗的子树,新树的深度为ha+1(或hb+1)。

/*
功能:并查集的算法和实现
作者:pussy
日期:2015-12-21
*/

# include<stdio.h>
# include<malloc.h>

//并查集节点类型定义
typedef struct{
    int data;//节点对应的数据
    int parent;//节点的双亲下标
    int rank;//节点的秩(用来表示以该节点为根节点的子树的高度)
}UFSTree;

void Union(UFSTree t[],int x,int y);
void Make_Set(UFSTree t[],int n);
int Find_Set(UFSTree t[],int x);

int main()
{
    int N,M,Q;
    printf("请输入人的个数和已知的亲戚对数\n");
    scanf("%d%d",&N,&M);
    UFSTree *t=(UFSTree *)malloc((N+1)*sizeof(UFSTree));
    Make_Set(t,N);
    int i,a,b;
    printf("每次输入两个数(a,b),代表已知a和b是亲戚关系(格式:a b)\n");
    for(i=1;i<=7;i++)
    {
        scanf("%d%d",&a,&b);
        Union(t,a,b);
    }
    printf("请输入Q(询问的次数):\n");
    scanf("%d",&Q);
    printf("请输入您的询问,(格式:a b),若a和b是亲戚,则输出YES,否则输出NO\n");
    for(i=1;i<=Q;i++)
    {
        scanf("%d%d",&a,&b);
        int pa=Find_Set(t,a);
        int pb=Find_Set(t,b);
        if(pa==pb)//在同一个集合里
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

//并查集树的初始化(每个元素都是一个单独的集合)
void Make_Set(UFSTree t[],int n)
{
    int i;
    for(i=1;i<=n;i++)
    {
        t[i].data=i;
        t[i].parent=i;
        t[i].rank=1;
    }
}

//查找一个元素所属集合
//每个集合有一个代表元素
//查找结果返回所属集合的代表元素
int Find_Set(UFSTree t[],int x)
{
    while(t[x].parent!=x)
    {
        x=t[x].parent;  
    }
    return x;
}

//两个元素所属的集合合并
void Union(UFSTree t[],int x,int y)
{
    int t1=Find_Set(t,x);//获取x所属集合的代表元素
    int t2=Find_Set(t,y);//获取y所属集合的代表元素
    if(t[t1].rank>t[t2].rank)
    {
        t[t2].parent=t1;
    }
    else if(t[t1].rank<t[t2].rank)
    {
        t[t1].parent=t2;
    }
    else
    {
        t[t1].parent=t2;
        t[t2].rank++;
        //或者t[t2].parent=t1;
        //t[t1].rank++;
    }
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值