leetcode
leetcode 1901 ~ 1950
根据描述创建二叉树

根据描述创建二叉树

难度:

标签:

题目描述

You are given a 2D integer array descriptions where descriptions[i] = [parenti, childi, isLefti] indicates that parenti is the parent of childi in a binary tree of unique values. Furthermore,

  • If isLefti == 1, then childi is the left child of parenti.
  • If isLefti == 0, then childi is the right child of parenti.

Construct the binary tree described by descriptions and return its root.

The test cases will be generated such that the binary tree is valid.

 

Example 1:

Input: descriptions = [[20,15,1],[20,17,0],[50,20,1],[50,80,0],[80,19,1]]
Output: [50,20,80,15,17,19]
Explanation: The root node is the node with value 50 since it has no parent.
The resulting binary tree is shown in the diagram.

Example 2:

Input: descriptions = [[1,2,1],[2,3,0],[3,4,1]]
Output: [1,2,null,null,3,4]
Explanation: The root node is the node with value 1 since it has no parent.
The resulting binary tree is shown in the diagram.

 

Constraints:

  • 1 <= descriptions.length <= 104
  • descriptions[i].length == 3
  • 1 <= parenti, childi <= 105
  • 0 <= isLefti <= 1
  • The binary tree described by descriptions is valid.

代码结果

运行时间: 325 ms, 内存: 23.8 MB


/*
 * 题目思路:
 * 1. 使用一个HashMap来存储所有节点值对应的TreeNode。
 * 2. 使用一个HashSet来记录所有有父节点的节点,这样可以通过遍历来确定根节点(即没有父节点的节点)。
 * 3. 使用Java Stream API来遍历descriptions数组,为每个节点创建TreeNode对象,更新父子关系。
 * 4. 最后从HashMap中获取根节点并返回。
 */
import java.util.*;
import java.util.stream.Collectors;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int val) { this.val = val; }
}

public class Solution {
    public TreeNode createBinaryTree(int[][] descriptions) {
        Map<Integer, TreeNode> map = new HashMap<>();
        Set<Integer> children = new HashSet<>();

        Arrays.stream(descriptions).forEach(desc -> {
            int parent = desc[0], child = desc[1], isLeft = desc[2];
            TreeNode parentNode = map.computeIfAbsent(parent, k -> new TreeNode(parent));
            TreeNode childNode = map.computeIfAbsent(child, k -> new TreeNode(child));

            if (isLeft == 1) {
                parentNode.left = childNode;
            } else {
                parentNode.right = childNode;
            }

            children.add(child);
        });

        return map.entrySet().stream()
                .filter(entry -> !children.contains(entry.getKey()))
                .map(Map.Entry::getValue)
                .findFirst()
                .orElse(null); // 应该不会执行到这里,因为保证了有效的二叉树
    }
}

解释

方法:

此题解首先通过遍历给定的描述数组来构造二叉树。针对每个描述项 [parenti, childi, isLefti],如果 parenti 和 childi 在之前未曾创建,则创建相应的 TreeNode 对象,并存储在一个字典中,以节点的值作为键。另外,还用一个字典来标记每个节点是否可能是根节点,即如果一个节点作为 child 出现过,那么它就不可能是根节点。最后,遍历这个可能的根节点的字典,找到唯一的标记为 True 的节点,即为根节点。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在构建二叉树的过程中,如果某个节点多次被标记为左或右子节点会如何处理?
在构建二叉树的过程中,如果某个节点多次被标记为同一子节点(即多次被标记为左子节点或右子节点),那么后面的标记将覆盖前面的标记。这是因为题解中的算法采用的是直接赋值的方式来设置子节点,没有进行是否已经设置的检查。这可能导致数据不一致或者逻辑错误,特别是在输入数据有误的情况下。
🦆
如何确保最终只有一个节点被标记为根节点?如果出现多个节点未被作为子节点引用,应如何处理?
题解中通过一个字典`isRoot`来标记每个节点是否可能是根节点,初始假设每个节点都可能是根节点,但如果一个节点作为任何其他节点的子节点出现,就将其标记为不可能是根节点。最后检查`isRoot`字典,寻找标记为True的节点即可确定根节点。如果理论上出现多个节点未被作为子节点引用,那么将违反二叉树的定义(一个树只能有一个根节点)。在实际实现中,如果发现有多个可能的根节点,应该抛出异常或错误,提示输入数据可能有误。
🦆
为什么在处理每个节点对时,都要判断节点是否已经在字典中存在?这一步骤对算法的效率有什么影响?
在处理每个节点对时,需要检查节点是否已经在字典中存在,以避免重复创建相同的节点。这是因为可能多次引用同一个节点(作为不同节点的子节点或多次引用作为父节点),如果不进行这种检查,就会创建多个具有相同值的节点实例,这会导致数据结构的不正确和资源的浪费。这一步骤会稍微增加计算开销,因为每次插入都需要检查字典,但由于字典的查找和插入操作平均时间复杂度为O(1),因此对整体算法效率的影响是可控的。
🦆
题解中提到的查找根节点的方法是否最优?存在更高效的寻找根节点的方法吗?
题解中的查找根节点的方法是通过一个遍历`isRoot`字典来寻找标记为True的节点。虽然这种方法简单明了,但在最坏情况下需要遍历整个字典。一种可能的优化方法是在构造过程中直接维护一个可能的根节点列表,每次修改节点的根节点属性时更新这个列表。但是,这种方法需要额外的逻辑来管理列表,可能会增加实现的复杂性。因此,当前的方法在大多数情况下已经足够高效,特别是考虑到二叉树通常节点数量不会特别大。

相关问题