题目描述:判断给出的边集能否构成一棵树。
解题思路:
- 不仅需要判断所有点是否属于一个集合,还要判断各个点是否符合树的定义。
- 而判断各点是否符合树的定义可以转换为判断它的入度是否符合要求,根结点的入度为0,而其余点的入度为1。
- 只要各个结点满足入度要求,只有一个根结点,以及各结点都属于同一集合,就可以构成一棵树。
代码
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
const int MAXN = 10001;
int father[MAXN]; // 父亲结点
int height[MAXN]; // 结点高度
void Initial(int n) { // 初始化
for(int i=1; i<=n; i++) {
father[i] = i; //每个结点的父亲为自己
height[i] = 0; // 每个结点的高度为0
}
}
int Find(int x) { // 查找根结点
if(x!=father[x])
father[x] = Find(father[x]); // 路径压缩
return father[x];
}
void Union(int x, int y) { // 合并集合
x = Find(x);
y = Find(y);
if(x!=y) { // 矮树作为高树的子树
if(height[x] < height[y])
father[x] = y;
else if(height[x] > height[y])
father[y] = x;
else {
father[y] = x;
height[x]++;
}
}
}
int main() {
int x,y,Case=0;
while(cin>>x>>y){
if(x==-1 && y==-1) break;
Case++;
if(x==0 && y==0){ // 树为空
cout<<"Case "<<Case<<" is a tree."<<endl;
continue;
}
Initial(MAXN);
int vis[MAXN] = {false}, flag=0;
map<int, int> myMap; // myMap[i]=j 记录值为i的结点,在输入中,是第j个出现的
int len;
while(x && y){
if(myMap[x]==0){
len = myMap.size();
myMap[x] = len;
}
if(myMap[y]==0){
len = myMap.size();
myMap[y] = len;
}
if(vis[myMap[y]]) flag=1; // 结点入度大于1
else vis[myMap[y]] = true;
Union(myMap[x],myMap[y]);
cin>>x>>y;
}
if(flag==1){ // 存在 结点入度大于1 的情况
cout<<"Case "<<Case<<" is not a tree."<<endl;
continue;
}
len = myMap.size();
int num=0;
for(int i=1;i<=len;i++){
if(!vis[i]) num++;
}
if(num!=1){ // 存在 入度为0的结点个数不为1 的情况
cout<<"Case "<<Case<<" is not a tree."<<endl;
continue;
}
int ans=0;
for(int i=1;i<=len;i++){
if(Find(i)==i) ans++;
}
if(ans==1) cout<<"Case "<<Case<<" is a tree."<<endl; // 结点属于一个集合
else cout<<"Case "<<Case<<" is not a tree."<<endl; // 结点不属于一个集合
}
return 0;
}