从前序与中序遍历序列构造二叉树

标签: 数组 哈希表 分治 二叉树

难度: Medium

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder 和 inorder 均 无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

Submission

运行时间: 224 ms

内存: 19.1 MB

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def build(pstart, pend, istart, iend):
            if pstart > pend:
                return
            
            root_val = preorder[pstart]
            index = istart
            for i in range(istart, iend+1):
                if inorder[i] == root_val:
                    index = i
                    break
            
            left_length = index - istart
            root = TreeNode(root_val)
            root.left = build(pstart+1, pstart+left_length, istart, istart+left_length)
            root.right = build(pstart+left_length+1, pend, istart+left_length+1, iend)
            return root
        
        return build(0, len(preorder)-1, 0, len(inorder)-1)

Explain

该题解使用递归的方式,根据前序遍历和中序遍历的特点构建二叉树。首先,前序遍历的第一个元素一定是根节点。然后,在中序遍历中找到根节点的位置,根据根节点将中序遍历序列分为左右两部分,分别对应根节点的左右子树。接着,递归地对左右子树进行同样的处理,直到所有节点都被处理完。

时间复杂度: O(n^2)

空间复杂度: O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def build(pstart, pend, istart, iend):
            if pstart > pend:
                return
            
            # 前序遍历的第一个元素为根节点
            root_val = preorder[pstart]
            
            # 在中序遍历中查找根节点的位置
            index = istart
            for i in range(istart, iend+1):
                if inorder[i] == root_val:
                    index = i
                    break
            
            # 计算左子树的节点数
            left_length = index - istart
            
            # 构建根节点
            root = TreeNode(root_val)
            
            # 递归构建左子树
            root.left = build(pstart+1, pstart+left_length, istart, istart+left_length)
            
            # 递归构建右子树
            root.right = build(pstart+left_length+1, pend, istart+left_length+1, iend)
            
            return root
        
        return build(0, len(preorder)-1, 0, len(inorder)-1)

Explore

不,当前的逻辑无法正确处理节点值存在重复的情况。在中序遍历中查找根节点的位置时,代码只查找并使用第一个匹配的节点。如果存在重复的节点值,这可能导致错误地划分左右子树。构造二叉树的算法通常假设树中的所有节点值是唯一的,因为这是二叉树中节点标识的基础。如果要处理重复值,需要其他机制来唯一确定节点位置,或者更改题目条件确保节点值的唯一性。

递归构建子树时,正确对应preorder和inorder数组中的元素范围至关重要。这通过以下几个步骤确保:1) 使用前序遍历的第一个元素确定根节点。2) 在中序遍历中找到该根节点的索引,这个索引将中序遍历分为左右两部分,对应左右子树。3) 根据中序遍历中左子树的长度来确定前序遍历中左子树和右子树的元素范围。4) 递归调用自身来构建左子树和右子树,每次传递正确更新的索引范围。这种方法保证了每次递归调用都正确地映射到子树的结构上。

是的,可以通过一些优化来减少重复操作或提高递归过程的效率。一个常见的改进是使用哈希表来存储中序遍历中每个值与其索引的映射,这样可以在O(1)时间内查找根节点的索引,而不是每次都进行线性搜索。此外,递归过程中可以通过更好地管理索引而不是复制数组片段来减少空间消耗。这些优化可以显著提高算法的时间和空间效率。