[剑指Offer] 07_重建二叉树

67 篇文章 0 订阅
42 篇文章 0 订阅

题目

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历都不含重复的数字。

例:

输入前序遍历序列{1, 2, 4, 7, 3, 5, 6, 8};中序遍历序列{4, 7, 2, 1, 5, 3, 8, 6},则重建为下面的二叉树,并返回根节点。
生成二叉树结构


思路

  1. 前序遍历输出的顺序是根节点-左树-右树,中序遍历输出的顺序是左树-根-右树。因此,我们可以从前序的顺序中找到根节点,用节点去中序中划分左树和右树。再对左树和右树递归处理。
    1. 时间复杂度:T(n) = T(n-1) + O(n),一次递归找到一个节点,寻找索引值需要O(n)。
    2. 空间复杂度:O(tree_height),递归的最大深度为树的高度,考虑只有左树或右树的最差情况,为O(n)。

代码

简单的写法:递归调用时,传入新列表(分片)。时间复杂度:O(n^2),空间复杂度O(n^2)

每次递归处理左子树和右子树时,向其传入了一个新列表,多使用了O(n)的空间。但是代码比较简洁。

def construct_binary_tree(preorder, inorder):
    """
    more space need but simple coding
    :param preorder:preorder traversal printout
    :param inorder:inorder traversal printout
    :return:root
    """
    def construct_recursion(preorder, inorder):
        if not preorder:
               return None
        root = TreeNode(preorder[0])
        if preorder[0] in inorder:
            # preorder not match inorder
            i = inorder.index(preorder[0])
        else:
            # print('This is not a binary tree!')
            # or raise exception
            nonlocal match_flag
            match_flag = False
            return None
        root.left = construct_recursion(preorder[1:i+1],inorder[:i])
        root.right = construct_recursion(preorder[i+1:], inorder[i+1:])
        return root
    if preorder:
        # empty input
        match_flag = True
        root = construct_recursion(preorder, inorder)
        return root if match_flag else print('Invalid Input')
    else:
        return print('EMPTY INPUT')

稍稍修改:递归调用时,传入索引值。时间复杂度:O(n),空间复杂度O(n)

def construct_binary_tree2(preorder, inorder):
    """
    do not trans new list
    :param preorder: preorder traversal printout
    :param inorder: inorder traversal printout
    :param left: left index of list of this child tree
    :param right: right index of this child tree
    :return: root
    """
    def construct_recursion(preorder, l_pre, r_pre, inorder, l_in, r_in):
        """
        :param preorder: preorder of binary tree
        :param l_pre: start index of subtree's preorder
        :param r_pre: end index of subtree's preorder
        :param inorder: inorder of binary tree
        :param l_in: start index of subtree's inorder
        :param r_in: end index of subtree's inorder
        :return: current node
        """

        if l_pre == r_pre:
            # single node & end recursion
            return TreeNode(preorder[l_pre])
        elif l_pre > r_pre:
            # prevent outing of range
            return None
        val = preorder[l_pre]
        root = TreeNode(val)
        if val in inorder[l_in:r_in+1]:
            i = inorder[l_in:r_in+1].index(val)
        else:
            raise Exception('Invalid Input: Not A Binary Tree')
        root.left = construct_recursion(preorder, l_pre+1, l_pre+i, inorder, l_in, l_in+i-1)
        root.right = construct_recursion(preorder, l_pre+i+1, r_pre, inorder, l_in+i+1, r_in)
        return root

    if preorder:
        root = construct_recursion(preorder, 0, len(preorder)-1, inorder, 0, len(inorder)-1)
        return root
    else:
        raise Exception('EMPTY INPUT!')

思考

思路想明白了代码还是比较容易完成的。麻烦的主要在于无效输入的处理。

  1. 空输入。
  2. 前序和中序中元素不相同。
  3. 前序和中序中元素相同,但顺序不匹配。

三种情况应当可以抛出异常,并在异常中能做不同描述。
我的实现中未使用异常,使得代码凌乱。用了nolocal关键词,修改了上层函数中的变量,感觉不是很好。

附TreeNode定义:

class TreeNode(object):

    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

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

  1. 代码1较慢:300ms
  2. 代码2优化了空间结构:200ms
  3. 而对于LeetCode,题目的输入必是一棵树,因此不用考虑处理输入构成不了树的情况。可以用下面的代码利用哈希表优化时间复杂度为O(n):64ms

代码

class Solution:
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        def construct_recursion(preorder, l_pre, r_pre, inorder, l_in, r_in):
            if l_pre == r_pre:
                # single node & end recursion
                return TreeNode(preorder[l_pre])
            elif l_pre > r_pre:
                # prevent outing of range
                return None
            val = preorder[l_pre]
            root = TreeNode(val)
            # if val in inorder[l_in:r_in+1]: no need to check
            i = index[val] # use hash table to find index in O(1)
            d_pre_in = i - l_in
            root.left = construct_recursion(preorder, l_pre+1, l_pre+ d_pre_in, inorder, l_in, i-1)
            root.right = construct_recursion(preorder, l_pre+ d_pre_in+1, r_pre, inorder, i+1, r_in)
            return root

        if preorder:
            index = {value:index for index, value in enumerate(inorder)} # key to optimize
            # O(n) to set up hash table
            root = construct_recursion(preorder, 0, len(preorder)-1, inorder, 0, len(inorder)-1)
            # O(n) to rebulid tree
            return root
            # total O(n)
        else:
            return None

类似题目

LeetCode 106. 从中序与后序遍历序列构造二叉树

题目

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

代码

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """
        def recursion(in_start, in_end, post_start, post_end):
            if in_start > in_end:
                return None          
            val = postorder[post_end]
            root = TreeNode(val)
            in_i = in_index[val]
            post_i = in_i - in_start + post_start - 1
            root.left = recursion(in_start, in_i-1, post_start, post_i)
            root.right = recursion(in_i+1, in_end, post_i+1, post_end-1)           
            return root
        if not inorder or not postorder:
            return None
        else:
            in_index = {value:index for index, value in enumerate(inorder)}
            return recursion(0, len(inorder)-1, 0, len(postorder)-1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值