并查集+DFS - hihoCoder #161 树结构判定

该博客讨论如何判断一个无向图是否为树结构。通过并查集算法和深度优先搜索(DFS),当所有节点的父节点指向同一节点时,表明图构成一棵树。如果满足N=M+1且所有点连通,则图是树的充分必要条件。博客提供了AC代码作为解决方案。

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

 

树结构判定

 

描述

给定一个包含 N 个顶点 M 条边的无向图 G ,判断 G 是不是一棵树。

输入

第一个是一个整数 T ,代表测试数据的组数。 (1 ≤ T ≤ 10)

每组测试数据第一行包含两个整数 N 和 M 。(2 ≤ N ≤ 500, 1 ≤ M ≤ 100000)

以下 M 行每行包含两个整数 a 和 b ,表示顶点 a 和顶点 b 之间有一条边。(1 ≤ a, b ≤ N)

输出

对于每组数据,输出YES或者NO表示 G 是否是一棵树。

 

分析

用并查集判断所有节点的父节点是否是一个,如果是那么就是一棵树,否则不是树形结构。

 

官方题解

 

本题要求判断给定的无向图是不是一棵树。

包含N个点M条边的无向图是树的充分必要条件是N=M+1且N个点连通。

所以本题的关键就是判断这N个点是不是连通在一起。

判断连通性一般也有两种方法。

一种是从一个点(比如1号点)开始进行DFS/BFS,搜索过程中把遇到的点都标记上,最后检查是不是N个点都被标记了。

另一种方法是用并查集,依次处理每一条边,把每条边的两个顶点都合并到一个集合里。最后检查是不是N个点都在同一个集合中。

并查集算法可以参考 hiho一下第14周 的提示。

 

AC代码

 

//并查集

#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

const int ma=510;
int fa[ma];

int findfa(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=findfa(fa[x]);
}

int main()
{
    int t;
    scanf("%d",&t);
    int n,m;
    while(t--)
    {
        scanf("%d%d",&n,&m);
        bool fg=false;
        if(m!=n-1) fg=true;

        for(int i=1;i<=n;++i) fa[i]=i;
        int x,y;
        while(m--)
        {
            scanf("%d%d",&x,&y);
            int fx=findfa(x),fy=findfa(y);
            if(fa[fx]!=fa[fy]) 
                fa[fx]=fa[fy];
        }

        if(fg)
        {
            printf("NO\n");
            continue;
        }

        int root=findfa(fa[1]);
        for(int i=2;i<=n;++i)
            if(findfa(fa[i])!=root)
        {
            fg=true;
            break;
        }

        if(fg) printf("NO\n");
        else printf("YES\n");
    }
    return 0;
}

 

//DFS,这里用的是链表型的数据结构

#include <stdio.h>
#include <string.h> 

struct _adj
{
	int v ;
	int next ;
} ;

struct _adj adj[205000] ;
bool visited[510] ;

int solve( int u, int parent )
{
	visited[u] = true ;
	int p = adj[u].next ;
	int ret = 1 ;
	while ( p != -1 )
	{
		if ( adj[p].v == parent )
		{
			p = adj[p].next ;
			continue ;
		}
		if ( visited[ adj[p].v ] )
			return 0 ;
		int tmp = solve( adj[p].v, u ) ;
		if ( tmp == 0 )
			return 0 ;
		else
			ret += tmp ;
		p = adj[p].next ;
	}
	return ret ;
}

int main()
{
	int t ;
	int n, m ;
	int i, j, k ;
	int adjused ;
	
	scanf( "%d", &t ) ;
	while ( t-- )
	{
		scanf( "%d %d", &n, &m ) ;
		adjused = n ;
		if ( m != n - 1 )
		{
			for ( k = 0 ; k < m ; ++k )
				scanf( "%d %d", &i, &j ) ;
			printf( "NO\n" ) ;
			continue ;
		}

		for ( i = 0 ; i < n ; ++i )
			adj[i].next = -1 ;
		for ( k = 0 ; k < m ; ++k )
		{
			scanf( "%d %d", &i, &j ) ;
			--i ; --j ;
			
			adj[ adjused ].v = j ;		
			adj[ adjused ].next = adj[i].next ;
			adj[i].next = adjused ;
			++adjused ;

			adj[ adjused ].v = i ;
			adj[ adjused ].next = adj[j].next ;
			adj[j].next = adjused ;
			++adjused ;
		}
		memset( visited, false, sizeof( visited ) ) ;
		if ( solve( 0, -1 ) != n )
			printf( "NO\n" ) ;
		else
			printf( "YES\n" ) ;

	}
	return 0 ;
}


#include <stdio.h>
#include <string.h> 

struct _adj
{
	int v ;
	int next ;
} ;

struct _adj adj[205000] ;
bool visited[510] ;

int solve( int u, int parent )
{
	visited[u] = true ;
	int p = adj[u].next ;
	int ret = 1 ;
	while ( p != -1 )
	{
		if ( adj[p].v == parent )
		{
			p = adj[p].next ;
			continue ;
		}
		if ( visited[ adj[p].v ] )
			return 0 ;
		int tmp = solve( adj[p].v, u ) ;
		if ( tmp == 0 )
			return 0 ;
		else
			ret += tmp ;
		p = adj[p].next ;
	}
	return ret ;
}

int main()
{
	int t ;
	int n, m ;
	int i, j, k ;
	int adjused ;
	
	scanf( "%d", &t ) ;
	while ( t-- )
	{
		scanf( "%d %d", &n, &m ) ;
		adjused = n ;
		if ( m != n - 1 )
		{
			for ( k = 0 ; k < m ; ++k )
				scanf( "%d %d", &i, &j ) ;
			printf( "NO\n" ) ;
			continue ;
		}

		for ( i = 0 ; i < n ; ++i )
			adj[i].next = -1 ;
		for ( k = 0 ; k < m ; ++k )
		{
			scanf( "%d %d", &i, &j ) ;
			--i ; --j ;
			
			adj[ adjused ].v = j ;		
			adj[ adjused ].next = adj[i].next ;
			adj[i].next = adjused ;
			++adjused ;

			adj[ adjused ].v = i ;
			adj[ adjused ].next = adj[j].next ;
			adj[j].next = adjused ;
			++adjused ;
		}
		memset( visited, false, sizeof( visited ) ) ;
		if ( solve( 0, -1 ) != n )
			printf( "NO\n" ) ;
		else
			printf( "YES\n" ) ;

	}
	return 0 ;
}

 

 

 

 

 

 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值