每日一道剑指offer-重建二叉树

题目:

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

例如,给出

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

    3
   / \
  9  20
    /  \
   15   7

限制:

0 <= 节点个数 <= 5000

解析:

这道题很有意思,利用了前序遍历和中序遍历的特点,有两种方法一种递归法,一种迭代法。

第一种:递归法,这也是大多数人的方法。

我们都知道:二叉树的前序遍历是:根节点-左子树-右子树;中序遍历是:左子树-根节点-右子树。那么先用前序遍历判断根节点,然后利用中序遍历中根节点的位置划分左右子树,例如:前序:[3,9,20,15,7],中序:[9,3,15,20,7],在前序中第一个元素是根节点3,在中序中找到3的位置,3的左边是左子树,右边是右子树。接着在前序中找左子树的根节点(就是root节点左边的第一个节点),继续在中序遍历中划分左右子树,以此类推。

在中序遍历中搜索根节点root的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。
根据中序遍历中的左 / 右子树的节点数量,可将 前序遍历 划分为 [ 根节点 | 左子树 | 右子树 ] 。

前序遍历划分 [ 3 | 9 | 20 15 7 ]
中序遍历划分 [ 9 | 3 | 15 20 7 ]

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

def buildTree(preorder, inorder) -> TreeNode:
    "通过前序遍历和中序遍历结果重建二叉树"
    if not preorder:  # 递归终止(当前序遍历为空时停止,证明节点都遍历完了)
        return None
    root = TreeNode(preorder[0])
    n = inorder.index(root.val)  # 找到根节点在中序遍历中的下标
    root.left = buildTree(preorder[1:n + 1], inorder[:n])  # 开启左子树递归
    root.right = buildTree(preorder[n + 1:], inorder[n + 1:])  # 开启右子树递归
    return root  # 回溯返回根节点

 

第二种:是leetcode官方的方法,迭代法。还是利用前序遍历和中序遍历的特点:前序遍历,从根节点root开始,只要有左子节点,就一直会往左下方走,直到最左下角。(根节点-左子树-右子树),而中序遍历,是从最左下角往上,如果碰到节点有右子节点,则会转向。(左子树-根节点-右子树)

所以按照这个思路:先利用前序遍历数组一直构建左子树,直到到底,然后利用中序遍历数组向上处理右子树。

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

def buildTree2(preorder, inorder) -> TreeNode:
    "通过前序遍历和中序遍历结果重建二叉树"
    if not preorder:
        return None
    stack = []
    # 初始时栈中存放了根节点(前序遍历的第一个节点),指针指向中序遍历的第一个节点;
    root = TreeNode(preorder[0])
    stack.append(root)
    inorderindex = 0
    # 依次枚举前序遍历中除了第一个节点以外的每个节点。
    for i in range(1, len(preorder)):
        preorderval = preorder[i]
        node = stack[-1]#获取栈顶元素
        if node.val != inorder[inorderindex]:  # (如果 index 和栈顶节点不同,我们将当前节点作为栈顶节点的左儿子;)
            # 用前序数组一直构建左子树,如果碰到了inorder[inorderIndex]表示到了左下角
            node.left = TreeNode(preorderval)
            stack.append(node.left)
        else:
            # while用于继续往上走,处理右节点。
            # 如果 index 恰好指向栈顶节点,那么我们不断地弹出栈顶节点并向右移动 index,
            while stack and stack[-1].val == inorder[inorderindex]:
                node = stack.pop()
                inorderindex += 1
            # 并将当前节点作为最后一个弹出的节点的右儿子;
            node.right = TreeNode(preorderval)
            stack.append(node.right)
    return root

注意:

用前序遍历数组进行构建的前提是前序遍历数组中的值不等于中序遍历数组中从第一个元素开始的值。因为中序遍历的第一个元素必定是最后的左子树(一定在最下角),要是两者相等,证明前序遍历数组已经到了最下角。然后开始往上走弹出左节点,继续判断栈中剩下的元素是否等于中序遍历中接下来的元素,相等则证明除了左子树外接下来还相等的点一定是根节点,接着处理右子树即可(将右子树赋值给根节点(有可能是局部的根节点,这里泛指左子树之上的节点))。

总结一下,其实就是当前序数组中和数组中序中第一次相同,肯定是到达最左边了-(左子节点),将左子节点出栈后剩下的元素还相等肯定是根节点(回退到根节点处理右子节点)。然后循环这个过程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值