数据结构课的一道题目:
问题
给定一个无向图,判断该图是否构成树。
输入
输入有若干测试样例。第一行是测试样例个数,接下来若干测试样例。
每个测试样例的第一行是结点数n,而且结点用1,2,..., n编号。
第二行是边数m,接下来是 m个结点对。
输出
如果一个图是树,则打印“YES",否则打印"NO"。每个输出占一行。
输入样例
3
3
2
1 2
2 3
3
3
1 2
2 3
1 3
3
1
2 3
输出样例
YES
NO
NO
首先,一个图能构成树需要满足以下三个条件:
① 边数 = 点数 - 1; ②没有回路也即没有环; ③没有孤立点即图是连通的;的
基于以上三个条件我们便有了以下思路:
1)在输入点数和边数的时候就开始判断二者的关系;
2)用一个set类型的容器dots来存已经连通的结点,访问到一条边时把未在dots中的点的存入dots中,若一条边的两个端点都在dots中,那么肯定会形成环(可以自己举例判断一下);
3)用一个set类型的容器visit来存已经访问过的结点,若visit的数目不等于给出的结点数目,说明有孤立点,图不是连通的。
代码如下:
/*思路:1.能构成树的图的条件:边数=点数-1,没有环,没有孤立的点也即通过边访问的点数与实际点数相等
2. 输入边数和点数时先判断条件1
3.用dots来存已经连通的点,说明里面的点已经构成路径,如果一条中的两个点都在dots里
说明会构成环,可以自己举几个例子,这是条件2的判断
4.用visit来存已经访问过的点,如果有孤立点,则visit的数目不等于点数
*/
#include<bits/stdc++.h>
using namespace std;
bool isTree(vector<vector<int>>&edges,int num_of_nodes){
set<int> dots; //存点的集合
set<int> visit; //访问过的点的集合
int num = edges.size();
for(int i = 0;i < num;++i){
vector<int> tmp = edges[i];
if(dots.find(tmp[0])!=dots.end()&&dots.find(tmp[1])!=dots.end())
return false;
if(dots.find(tmp[0])==dots.end()){
dots.insert(tmp[0]);
visit.insert(tmp[0]);
}
if(dots.find(tmp[1])==dots.end()){
dots.insert(tmp[1]);
visit.insert(tmp[1]);
}
}
if(visit.size() != num_of_nodes) return false;
return true;
}
int main(){
int t; cin>>t; //测试样例个数
while(t--){
vector<vector<int>> edges;
int num_of_nodes; cin>>num_of_nodes;
int num_of_edges; cin>>num_of_edges;
for(int i = 0;i < num_of_edges;++i){
int n1,n2; cin>>n1>>n2;
vector<int> edges_i;
edges_i.push_back(n1); edges_i.push_back(n2);
edges.push_back(edges_i);
}
if(num_of_edges != num_of_nodes - 1){
cout<<"NO"<<endl;
continue;
}
if(isTree(edges,num_of_nodes))
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}