给定有向图,判断是否能唯一构成一颗二叉树
典型错误思路:只根据入度判断(完美踩坑!!气死了),因为有向图中可能非联通或者存在环
正确思路:
- 先用DFS / BFS / 并查集判断是否是联通图
- 再通过边数和入度来判断即可,满足边数 = n - 1的连通图一定不存在环,一定是树
class Solution {
public:
void DFS(int x, vector<int>& leftChild, vector<int>& rightChild, bool vis[])
{
if(vis[x]) return;
vis[x] = 1;
if(leftChild[x] != -1) DFS(leftChild[x], leftChild, rightChild, vis);
if(rightChild[x] != -1) DFS(rightChild[x], leftChild, rightChild, vis);
}
bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
if(n == 1) return true; // 特例,单独处理
int indegree[n], e = 0;
bool vis[n];
memset(indegree, 0, sizeof(indegree));
memset(vis, 0, sizeof(vis));
for(int i = 0; i < n; ++i)
if(leftChild[i] != -1 || rightChild[i] != -1)
{
DFS(i, leftChild, rightChild, vis);
break; // 注意DFS一个有效的即可
}
for(int i = 0; i < n; ++i)
{
if(!vis[i]) return false; // 若还有节点未访问到则非联通
if(leftChild[i] != -1) ++indegree[leftChild[i]], ++e;
if(rightChild[i] != -1) ++indegree[rightChild[i]], ++e;
}
for(int i = 0; i < n; ++i)
if(indegree[i] > 1) // 若入度大于1
return false;
return e == n - 1;
}
};
他人较为优秀的思路:只用并查集,在遍历的同时判断
若有两个节点父节点相同,或者某个节点有2个以上结点指向,则直接返回
最后判断连通块的个数即可
class Solution {
public:
static const int N = 1e4 + 7;
int p[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
for (int i = 0; i < n; i ++) p[i] = i;
for (int i = 0; i < n; i ++) {
int Left = leftChild[i];
if (Left != -1) {
int pa = find(i), pb = find(Left);
if (pa == pb) return false; // 祖先相同
else {
if (pb == Left)
p[pb] = pa;
else // 左孩子的父亲结点已经被修改,说明左孩子已经被别的结点修改了
return false;
}
}
int Right = rightChild[i];
if (Right != -1) {
int pa = find(i), pb = find(Right);
if (pa == pb) return false;
else {
if (pb == Right)
p[pb] = pa;
else
return false;
}
}
}
// 统计连通块个数
int ans = 0;
for (int i = 0; i < n; i ++) {
if (p[i] == i) ans ++;
}
return ans == 1;
}
};