判断一个图是不是树
问题描述
给一个以0 0结尾的整数对列表,除0 0外的每两个整数表示一条连接了这两个节点的边。假设节点编号不超过100000大于0。你只要判断由这些节点和边构成的图是不是树。是输出YES,不是输出NO。

输入样例1
in
6 8 5 3 5 2 6 4 5 6 0 0
输出样例1
in
YES
输入样例2
in
8 1 7 3 6 2 8 9 7 5 7 4 7 8 7 6 0 0
输出样例2
in
YES
输入样例3
in
3 8 6 8 6 4 5 3 5 6 5 2 0 0
输出样例3
in
NO
输入样例4
in
1 2 3 4 0 0
输出样例4
in
NO
输入样例5
空树也是树
cpp
0 0
输出样例5
in
YES
c++代码
cpp
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
int a, b, cont = 0;
int arr[100001];
bool key = true;
unordered_set<int> st;
int myfind(int x) {
int root = x;
while(root != arr[root]) root = arr[root];
int i = x, j;
while(i != root) {
j = arr[i];
arr[i] = root;
i = j;
}
return root;
}
void mymerge(int x, int y) {
x = myfind(x), y = myfind(y);
if (x != y) arr[y] = arr[x];
}
int main() {
for (int i = 1; i <= 100000; i++) arr[i] = i;
while(true) {
scanf("%d %d", &a, &b);
if (a == 0 && b == 0) {
if (cont == 0) {
printf("YES\n");
return 0;
}
if (key && st.size() != cont + 1) key = false;
if (key) {
int root = -1;
for (int x : st) {
int k = myfind(x);
if (root == -1) root = k;
if (k != root) {
key = false;
break;
}
}
}
if (key) printf("YES\n");
else printf("NO\n");
break;
}
else {
cont++;
if (st.find(a) == st.end()) st.insert(a);
if (st.find(b) == st.end()) st.insert(b);
if (key) {
int x = myfind(a), y = myfind(b);
if (x == y) key = false;
else mymerge(a, b);
}
}
}
return 0;
}
算法解析
满足下面三个条件的图是树
1、不存在环。
2、所有点都是互相连通的。
3、点数=边数 + 1。
判断环
用并查集,给每个点一个初始的编号,并初始化所有节点的父亲为本身。每新加入一条边就把边相连的两个集合合并到一起,如果边相连的集合原本就是同一个,说明已经成环,不是生成树。
cpp
int x = myfind(a), y = myfind(b);
if (x == y) key = false; //边相连的集合原本就是同一个,说明已经成环,不是生成树。
else mymerge(a, b); //否则合并一下
判断节点数和边数的关系
把点存在一个unordered_set里面就行,因为不能存重复的,边用一个cont就可以存
cpp
cont++;
if (st.find(a) == st.end()) st.insert(a);
if (st.find(b) == st.end()) st.insert(b);
......
if (key && st.size() != cont + 1) key = false;
判断连通性
如果是连通的,根据我们之前的合并操作,每一个节点应该都属于同一个集合,如果不是,则不是树。
cpp
int root = -1;
for (int x : st) {
int k = myfind(x);
if (root == -1) root = k;
if (k != root) {
key = false;
break;
}
}