在问 · 算法题解

889. 根据前序和后序遍历构造二叉树

中等二叉树递归分治 + 哈希表定位左子树大小力扣原题 ↗

题目描述

输入两个整数数组 preorder 和 postorder,分别是同一棵二叉树的前序遍历和后序遍历结果,树中节点值互不相同。

根据它们还原出这棵二叉树并返回根节点,结果以层序数组表示。

仅凭前序和后序可能无法唯一确定一棵树,这时返回任意一棵符合两种遍历的树即可。

示例 1
输入:preorder = [1,2,4,5,3,6,7], postorder = [4,5,2,6,7,3,1]
输出:[1,2,3,4,5,6,7]

解释:还原出的是一棵满二叉树:根 1,第二层 2、3,第三层 4、5、6、7。

示例 2
输入:preorder = [1], postorder = [1]
输出:[1]

解释:只有一个节点。

数据范围

  • 1 <= preorder.length <= 30
  • 1 <= preorder[i] <= preorder.length
  • preorder 中的值互不相同。
  • postorder.length == preorder.length
  • 1 <= postorder[i] <= postorder.length
  • postorder 中的值互不相同。
  • 保证 preorder 和 postorder 来自同一棵二叉树。

解题思路:递归分治 + 哈希表定位左子树大小

前序的第一个数是根,第二个数是左子树的根。左子树的根在后序里是左子树的最后一个元素,所以它在后序中的位置能告诉我们左子树有多少个节点 size。据此把前序和后序都切成“根 | 左子树 | 右子树”三段,递归构造。只有一个孩子时无法区分左右,约定把它当作左孩子。

为什么正确

build(pl, pr, ql) 处理的前序片段 preorder[pl..pr] 与后序片段 postorder[ql..ql+(pr-pl)] 描述的是同一棵子树。

复杂度分析

时间 O(n)(哈希表查位置),空间 O(n)。

易错点

必须先判断 pl == pr(只剩一个节点)再访问 preorder[pl + 1],否则会越界或误用右侧子树的元素。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def constructFromPrePost(self, preorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        pos = {v: i for i, v in enumerate(postorder)}
        def build(pl, pr, ql):
            if pl > pr:
                return None
            root = TreeNode(preorder[pl])
            if pl == pr:
                return root
            size = pos[preorder[pl + 1]] - ql + 1
            root.left = build(pl + 1, pl + size, ql)
            root.right = build(pl + size + 1, pr, ql + size)
            return root
        return build(0, len(preorder) - 1, 0)

载入编辑器,自己跑一遍 →

其他解法

分治法先定位左子树大小再递归,是构造类题目的通用模板;栈模拟法把前序看成“进入结点”、后序看成“离开结点”,一遍扫描完成构造。

解法 2:栈模拟(前序入栈、后序出栈) 时间 O(n),空间 O(h)

按前序依次创建结点,栈中保存从根到当前位置还没构造完的结点。新结点一定是栈顶的孩子:栈顶没有左孩子就挂在左边,否则挂在右边,然后入栈。用指针 j 指向后序中下一个应该结束的结点:只要栈顶等于 postorder[j],说明它的子树已构造完,弹出并把 j 加 1。

不需要哈希表和下标区间计算,空间只和树高有关,也不会因递归过深而栈溢出。但“后序决定何时出栈”的正确性不如分治直观,需要理解两种遍历的时序关系。

class Solution:
    def constructFromPrePost(self, preorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        stack = []
        j = 0
        for v in preorder:
            node = TreeNode(v)
            if stack:
                if stack[-1].left is None:
                    stack[-1].left = node
                else:
                    stack[-1].right = node
            stack.append(node)
            while stack and stack[-1].val == postorder[j]:
                root = stack.pop()
                j += 1
        return root

二叉树 专题的其他题目

查看 二叉树 全部题目 →