并查集的反思-----小希的迷宫与Is It a Tree?

本文深入探讨了并查集算法的应用场景与实现细节,通过具体案例解析如何利用并查集解决图论问题,如判断图是否成树及连通性等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
事实证明半斤八两的理解忘记的速度会很快,今天早上起来忽然想起来这个题(因为昨天碰到一个欧拉回路+并查集+字典树),已然思路不清晰了。
是什么什么让我认识并查集的呢?是Hdu的畅通工程,给出一些列已经连通的点,要你判断还需要多少建多少条路使保持畅通。我们解决的办法是将能够保持连通转化为所有的点位于一个集合,并且将他们的祖先序号规定为最小元素的值。然后还需要建的路即是不相交的集合。然后我们又可以做这样一类题,图是否连通,这类题,最小生成树也可以做。而小希的迷宫呢?小希的迷宫是在保持原有图形连通的基础上加入了一个新的条件---不成环。在思考不成环这个问题时,我注意到了原来一直没有透彻理解的问题---路径压缩。如果合并的两个点具有公共祖先,那么将形成以个环。然后,又碰到了这道经典的题目,is it a tree?这个图在小希的迷宫上又增加了一个条件---有向图,很自然不可能由一个节点的子孙走回父亲节点,所以判断入度为1的条件---即不走回头路 find(i)=i.
并查集目前为止一般包括三个函数
void union()//跟据你的输入合并2个点
int find()//寻找祖先+路径压缩
void make()//初始化



无向图的并查集
1.不成回路
2.联通
*/
#include<stdio.h>
#include<string.h>
#define maxn 100002
#define max(a,b) (a)>(b)?(a):(b)
int flag[100001];
int bin[100001];
int ans;
int Find(int x)
{
	while(bin[x]!=x)
		x=bin[x];
	return x;
}
void Union(int x,int y)
{
	flag[x]=flag[y]=1;
	x=Find(x);
	y=Find(y);
	if(x!=y)
		bin[x]=y;
	else
		ans=0;//如果祖先相同,则构成回路。太重要了。
}
int main()
{
	int a,b,i;
	while(scanf("%d%d",&a,&b)!=EOF &&(a!=-1 || b!=-1))
	{
		memset(flag,0,sizeof(flag));
		int count=0;
		if(a==0 && b==0)
		{
			printf("Yes\n");
			continue;
		}
		for(i=0;i<=100001;i++)
			bin[i]=i;
			//bin[i]=maxn;
		//bin[a]=a;
		//bin[b]=b;
		Union(a,b);
		ans=1;
		while(scanf("%d%d",&a,&b),a,b)
		{
			//bin[a]=a;
			//bin[b]=b;
			Union(a,b);
		}
		for(i=1;i<=100001;i++)
			if(flag[i]&&bin[i]==i)
				count++;
		if(count>1)
				ans=0;//这个判断是否联通。
			
		if(ans)
				printf("Yes\n");
		else
			printf("No\n");
	}
return 0;
}




hdu 1325
/*    并查集
题意:输入每两个数a,b, a指向b(即:a为b的父亲),输入0,0代表一棵树结束;
思路:判断是否为树;
  1、空树是一棵树
  2、除了根节点,每个节点入度必须为1
  3、不能有圈
 4、不能有连通分量
*/
#include<stdio.h>
#define N 10005

int father[N],mark[N];

void MakeSet()//初始化
{
    for(int i=0;i<N;i++)
    {father[i]=i;
    mark[i]=0;}
}

int FindSet(int x)
{
    if(x!=father[x])
        father[x]=FindSet(father[x]);//没有路径压缩实践证明超时
    return father[x];
}

bool Union(int a,int b)
{
    int x=FindSet(a);
    int y=FindSet(b);
    if(x==y)
        return false;
    else
        father[y]=x;//被卡了,注意接的顺序
    return true;
}

int main()
{
    int s,e;
    int can=1;
    while(scanf("%d%d",&s,&e),s>=0&&e>=0)
    {
        MakeSet();
        Union(s,e);
        bool judge=true;
        if(s==0&&e==0)//空树也满足条件
        {
            printf( "Case %d is a tree.\n",can++);
            continue;
        }
        mark[s] = mark[e] = 1;
        while(scanf("%d%d",&s,&e),s||e)
        {
            mark[s]=mark[e]=1;
            if(FindSet(e)==e)//判断节点入度是否为1
            {
              judge*=Union(s,e);//判断是否有圈
            }
            else
                judge=false;
        }

        int flag=0;
        for(int i=0;i<N;i++)
        {
            if(mark[i])
            {
                if(FindSet(i)==i)
                    flag++;
            }
        }
        if(flag==1&&judge)
        printf( "Case %d is a tree.\n",can++);
        else
        printf( "Case %d is not a tree.\n",can++);
        }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值