leetcode算法学习(18)——重建二叉树

举例

例如要重建的是如下二叉树。
在这里插入图片描述
其前序遍历和中序遍历如下。

preorder = [3,9,8,5,4,10,20,15,7]
inorder = [4,5,8,10,9,3,15,20,7]

前序遍历的第一个元素 3 是根节点,第二个元素 9 可能位于左子树或者右子树,需要通过中序遍历判断。
中序遍历的第一个元素是 4 ,不是根节点 3,说明 9 位于左子树,因为根节点不是中序遍历中的第一个节点。同理,前序遍历的后几个元素 8、5、4 也都位于左子树,且每个节点都是其上一个节点的左子节点。
前序遍历到元素 4,和中序遍历的第一个元素相等,说明前序遍历的下一个元素 10 位于右子树。那么 10 位于哪个元素的右子树?从前序遍历看,10 可能位于 4、5、8、9、3 这些元素中任何一个元素的右子树。从中序遍历看,10 在 8 的后面,因此 10 位于 8 的右子树。把前序遍历的顺序反转,则在 10 之前的元素是 4、5、8、9、3,其中 8 是最后一次相等的节点,因此前序遍历的下一个元素位于中序遍历中最后一次相等的节点的右子树。
同理可知,20 位于 3 的右子树,15 和 7 分别是 20 的左右子节点。

题目描述

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

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:
在这里插入图片描述

方法一:递归–复制列表的形式

思路

前序遍历特点: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序,以题目示例为例:[ 3 | 9 | 20 15 7 ]
中序遍历特点: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序,以题目示例为例:[ 9 | 3 | 15 20 7 ]
根据题目描述输入的前序遍历和中序遍历的结果中都不含重复的数字,其表明树中每个节点值都是唯一的。
根据以上特点,可以按顺序完成以下工作:
前序遍历的首个元素即为根节点 root 的值
在中序遍历中搜索root 的索引并划分为 [ 左子树 | 根节点 | 右子树 ]
根据中序遍历中的左(右)子树的节点数量,可将前序遍历划分为 [ 根节点 | 左子树 | 右子树 ]
自此可确定三个节点的关系 :

1.树的根节点
2.左子树根节点
3.右子树根节点

即前序遍历中左右子树的首个元素。
子树特点: 子树的前序和中序遍历仍符合以上特点,以题目示例的右子树为例:前序遍历:[20 | 15 | 7],中序遍历 [ 15 | 20 | 7 ] 。
根据子树特点,我们可以通过同样的方法对左右子树进行划分,每轮可确认三个节点的关系 。此递推性质让我们联想到用递归方法 处理。

代码

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if len(inorder)==0:
            return None
        #获取根节点
        root=TreeNode(preorder[0])
        #获取根节点在中序遍历中的索引
        idx=inorder.index(preorder[0])
        #左子树,由idx可得左子树长度为idx,前序遍历中第一个为root,因此preorder[1:idx+1]为左子树部分,剩余的为右子树
        root.left=self.buildTree(preorder[1:idx+1],inorder[:idx])
        #右子树
        root.right=self.buildTree(preorder[idx+1:],inorder[idx+1:])
        return root

方法二:递归–哈希表

思路

  • 递归解析:
    • 前序遍历中根节点的索引pre_root、中序遍历最左边界left、中序遍历最右边界right
    • 终止条件: 当 left > right ,子树中序遍历为空,说明已经越过叶子节点,此时返回 null 。
  • 建立根节点root: 值为前序遍历中索引为pre_root的节点值。
  • 搜索根节点root在中序遍历的索引 i:使用哈希表 dic 预存储中序遍历的值与索引的映射关系,每次搜索的时间复杂度为 O(1),提升搜索效率。
  • 构建根节点root的左子树和右子树: 通过调用 recur() 方法开启下一层递归
    • 左子树: 前序遍历中左子树的根节点索引为 pre_root+ 1 ,中序遍历的左右边界分别为 left 和 i - 1。
    • 右子树: 前序遍历中右子树的根节点索引为 i - left + pre_root+ 1(即:根节点索引 + 左子树长度 + 1),中序遍历的左右边界分别为 i + 1 和 right。
  • 返回值: 返回 root,含义是当前递归层级建立的根节点 root 为上一递归层级的根节点的左或右子节点。

代码

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        self.dic,self.preorder={},preorder
        #for index,item in enumerate(inorder):
        #    self.dic[item]=index
         for i in range(len(inorder)):
             self.dic[inorder[i]]=i
        return self.recur(0,0,len(inorder)-1)
    
    def recur(self,pre_root,left,right):
        if left>right:return 
        root=TreeNode(self.preorder[pre_root])
        i=self.dic[self.preorder[pre_root]]
        root.left = self.recur(pre_root + 1 , left , i - 1)
        root.right = self.recur(pre_root + 1 + i - left , i + 1 , right)
        return root
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值