Medium

题目描述

你有 n 个二叉树的节点,节点编号从 0 到 n - 1,其中节点 i 的两个子节点分别是 leftChild[i] 和 rightChild[i],如果给定的所有节点恰好组成一个有效的二叉树,则返回 true,否则返回 false。

如果节点 i 没有左子节点,那么 leftChild[i] 就等于 -1。对于右子节点也是如此。

注意:节点没有值,在这个问题中我们只使用节点编号。

示例 1:

输入: n = 4, leftChild = [1,-1,3,-1], rightChild = [2,-1,-1,-1]
输出: true

示例 2:

输入: n = 4, leftChild = [1,-1,3,-1], rightChild = [2,3,-1,-1]
输出: false

示例 3:

输入: n = 2, leftChild = [1,0], rightChild = [-1,-1]
输出: false

提示:

  • n == leftChild.length == rightChild.length
  • 1 <= n <= 10^4
  • -1 <= leftChild[i], rightChild[i] <= n - 1

解题思路

这道题需要验证给定的节点能否构成一个有效的二叉树。一个有效的二叉树必须满足以下条件:

  1. 唯一根节点:有且仅有一个节点没有父节点(即根节点)
  2. 无重复父子关系:每个节点最多只能有一个父节点
  3. 连通性:所有节点都必须连通,即能通过根节点访问到所有节点
  4. 无环:不能存在环路

解题思路:

首先统计每个节点的入度(有多少个父节点指向它)。在有效的二叉树中,根节点入度为0,其他节点入度为1。

然后从根节点开始进行DFS或BFS遍历,检查是否能访问到所有n个节点。如果访问的节点数量等于n,说明所有节点都连通;否则存在孤立的节点或环路。

主要检查点:

  • 入度为0的节点数量必须为1(唯一根节点)
  • 入度大于1的节点不能存在(违反树的性质)
  • 从根节点能访问到所有节点(连通性)

这种方法时间复杂度为O(n),空间复杂度为O(n),是最优解法。

代码实现

class Solution {
public:
    bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
        vector<int> indegree(n, 0);
        
        // 计算每个节点的入度
        for (int i = 0; i < n; i++) {
            if (leftChild[i] != -1) {
                indegree[leftChild[i]]++;
                if (indegree[leftChild[i]] > 1) return false; // 一个节点不能有多个父节点
            }
            if (rightChild[i] != -1) {
                indegree[rightChild[i]]++;
                if (indegree[rightChild[i]] > 1) return false; // 一个节点不能有多个父节点
            }
        }
        
        // 找到根节点(入度为0)
        int root = -1;
        for (int i = 0; i < n; i++) {
            if (indegree[i] == 0) {
                if (root != -1) return false; // 不能有多个根节点
                root = i;
            }
        }
        
        if (root == -1) return false; // 必须有一个根节点
        
        // 从根节点开始DFS,检查是否能访问所有节点
        vector<bool> visited(n, false);
        function<void(int)> dfs = [&](int node) {
            if (node == -1 || visited[node]) return;
            visited[node] = true;
            dfs(leftChild[node]);
            dfs(rightChild[node]);
        };
        
        dfs(root);
        
        // 检查是否所有节点都被访问
        for (int i = 0; i < n; i++) {
            if (!visited[i]) return false;
        }
        
        return true;
    }
};
class Solution:
    def validateBinaryTreeNodes(self, n: int, leftChild: List[int], rightChild: List[int]) -> bool:
        indegree = [0] * n
        
        # 计算每个节点的入度
        for i in range(n):
            if leftChild[i] != -1:
                indegree[leftChild[i]] += 1
                if indegree[leftChild[i]] > 1:  # 一个节点不能有多个父节点
                    return False
            if rightChild[i] != -1:
                indegree[rightChild[i]] += 1
                if indegree[rightChild[i]] > 1:  # 一个节点不能有多个父节点
                    return False
        
        # 找到根节点(入度为0)
        root = -1
        for i in range(n):
            if indegree[i] == 0:
                if root != -1:  # 不能有多个根节点
                    return False
                root = i
        
        if root == -1:  # 必须有一个根节点
            return False
        
        # 从根节点开始DFS,检查是否能访问所有节点
        visited = [False] * n
        
        def dfs(node):
            if node == -1 or visited[node]:
                return
            visited[node] = True
            dfs(leftChild[node])
            dfs(rightChild[node])
        
        dfs(root)
        
        # 检查是否所有节点都被访问
        return all(visited)
public class Solution {
    public bool ValidateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
        int[] indegree = new int[n];
        
        // 计算每个节点的入度
        for (int i = 0; i < n; i++) {
            if (leftChild[i] != -1) {
                indegree[leftChild[i]]++;
                if (indegree[leftChild[i]] > 1) return false; // 一个节点不能有多个父节点
            }
            if (rightChild[i] != -1) {
                indegree[rightChild[i]]++;
                if (indegree[rightChild[i]] > 1) return false; // 一个节点不能有多个父节点
            }
        }
        
        // 找到根节点(入度为0)
        int root = -1;
        for (int i = 0; i < n; i++) {
            if (indegree[i] == 0) {
                if (root != -1) return false; // 不能有多个根节点
                root = i;
            }
        }
        
        if (root == -1) return false; // 必须有一个根节点
        
        // 从根节点开始DFS,检查是否能访问所有节点
        bool[] visited = new bool[n];
        
        void DFS(int node) {
            if (node == -1 || visited[node]) return;
            visited[node] = true;
            DFS(leftChild[node]);
            DFS(rightChild[node]);
        }
        
        DFS(root);
        
        // 检查是否所有节点都被访问
        for (int i = 0; i < n; i++) {
            if (!visited[i]) return false;
        }
        
        return true;
    }
}
var validateBinaryTreeNodes = function(n, leftChild, rightChild) {
    const indegree = new Array(n).fill(0);
    
    // 计算每个节点的入度
    for (let i = 0; i < n; i++) {
        if (leftChild[i] !== -1) {
            indegree[leftChild[i]]++;
            if (indegree[leftChild[i]] > 1) return false; // 一个节点不能有多个父节点
        }
        if (rightChild[i] !== -1) {
            indegree[rightChild[i]]++;
            if (indegree[rightChild[i]] > 1) return false; // 一个节点不能有多个父节点
        }
    }
    
    // 找到根节点(入度为0)
    let root = -1;
    for (let i = 0; i < n; i++) {
        if (indegree[i]

复杂度分析

复杂度类型复杂度说明
时间复杂度O(n)需要遍历所有节点计算入度,然后进行一次DFS遍历
空间复杂度O(n)需要额外的入度数组、访问标记数组,以及递归调用栈空间