【剑指Offer】第7题-重建二叉树-Python

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。(事实上,返回的是二叉树的根节点)

LeetCode

类比LeetCode题目有105. 从前序与中序遍历序列构造二叉树

思路

前序 [1,2,4,7,3,5,6,8]
后序 [4,7,2,15,3,8,6]

前序遍历的第一个是根节点1,扫描中序遍历结果,就可以找到根节点1的位置。在中序遍历中,根节点1左边的数字位于左子树,1右边的节点位于右子树。这样就可以划分出根节点和对应的孩子。
同样地,在左子树和右子树当中,前序遍历的第一个节点是根节点。可以用上面同样的方法去构建,这样也就使用递归的方法来构建二叉树。

代码

    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        #递归终止条件 任何一种遍历为空
        if not preorder or not inorder:
            return None
        #前序第一个为root
        root = TreeNode(preorder[0])
        #找到中序遍历中root的位置
        index = inorder.index(preorder[0])
        # 递归调用 求左子树 右子树
        # 左子树为 前序中根节点后一个后面index个数 中序中从开始到根节点
        root.left = self.buildTree(preorder[1:index+1], inorder[:index])
        # 右子树为 前序中从index+1后面的 中序从根节点后一个开始到最后
        root.right = self.buildTree(preorder[index+1:], inorder[index+1:])
        return root

扩展1 中序后序建立二叉树

在这里插入图片描述
LeetCode地址:
106. 从中序与后序遍历序列构造二叉树

思路分析

使用递归来解决问题
首先确定根节点,后序遍历的最后一个元素是根节点。根据根节点在中序遍历中的位置,可以将中序遍历分割为左子树和右子树。
同样地,左子树的节点个数可以通过中序遍历中的根节点位置来确定。中序遍历中从最开始到根节点的长度就是左子树的个数。

code

def buildTree(self, inorder: 'List[int]', postorder: 'List[int]') -> 'TreeNode':
    if not inorder or not postorder:
        return None
    root = TreeNode(postorder[-1])       # 后序遍历的最后一个节点是根节点
    index = inorder.index(postorder[-1]) # 找到中序遍历中的根节点位置
    root.left = self.buildTree(inorder[:index],postorder[:index])
    root.right = self.buildTree(inorder[index+1:],postorder[index:-1])
    return root

扩展2 前序后序建立二叉树

在这里插入图片描述
需要注意的是:前序和后序并不能够唯一确定一颗二叉树。
LeetCode地址:
889. 根据前序和后序遍历构造二叉树

算法思想

思路

前序遍历为:

(root node) (preorder of left branch) (preorder of right branch)

而后序遍历为:

(postorder of left branch) (postorder of right branch) (root node)

例如,如果最终的二叉树可以被序列化的表述为 [1, 2, 3, 4, 5, 6, 7],那么其前序遍历为 [1] + [2, 4, 5] + [3, 6, 7],而后序遍历为 [4, 5, 2] + [6, 7, 3] + [1].
如果我们知道左分支有多少个结点,我们就可以对这些数组进行分组,并用递归生成树的每个分支。

算法

我们令左分支有 L 个节点。我们知道左分支的头节点为 pre[1],但它也出现在左分支的后序表示的最后。所以 pre[1] = post[L-1](因为结点的值具有唯一性),因此 L = post.indexOf(pre[1]) + 1。

现在在我们的递归步骤中,左分支由 pre[1 : L+1] 和 post[0 : L] 重新分支,而右分支将由 pre[L+1 : N] 和 post[L : N-1] 重新分支。

Code

def constructFromPrePost(self, pre, post):
    if not pre: return None
    root = TreeNode(pre[0])
    if len(pre) == 1: return root

    L = post.index(pre[1]) + 1
    root.left = self.constructFromPrePost(pre[1:L+1], post[:L])
    root.right = self.constructFromPrePost(pre[L+1:], post[L:-1])
    return root

复杂度分析

  • 时间复杂度:O(N^2),其中 NN 是结点的数目。

  • 空间复杂度:O(N),答案所用去的空间。

方法二:递归(节约空间的变体)

我们这里提出一个方法一的变体,使用索引指代子数组 pre 和 post,而不是通过那些子数组的副本。这里,(i0, i1, N) 指的是 pre[i0:i0+N], post[i1:i1+N].

def constructFromPrePost(self, pre, post):
       def make(i0, i1, N):
           if N == 0: return None
           root = TreeNode(pre[i0])
           if N == 1: return root

           for L in xrange(N):
               if post[i1 + L - 1] == pre[i0 + 1]:
                   break

           root.left = make(i0 + 1, i1, L)
           root.right = make(i0 + L + 1, i1 + L, N - 1 - L)
           return root

       return make(0, 0, len(pre))

复杂度分析

  • 时间复杂度:O(N^2),其中 N是结点的数目。

  • 空间复杂度:O(N),答案所用去的空间。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值