数据研发笔试Leetcode刷题笔记05:重建二叉树

文章目录

  • 1 题目描述
  • 2 解题思路
  • 3 代码实现(Python3)
  • 4 复杂度分析

1 题目描述

来源:力扣(LeetCode)

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
数据研发笔试Leetcode刷题笔记05:重建二叉树_第1张图片

2 解题思路

题目分析:

  • 前序遍历特点: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序,以题目示例为例:[ 3 | 9 | 20 15 7 ]
  • 中序遍历特点: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序,以题目示例为例:[ 9 | 3 | 15 20 7 ]

根据题目描述输入的前序遍历和中序遍历的结果中都不含重复的数字,其表明树中每个节点值都是唯一的1

根据以上特点,可以按顺序完成以下工作:

(1)前序遍历的首个元素即为根节点 root 的值;
(2)在中序遍历中搜索根节点 root 的索引 ,可将中序遍历划分为 [ 左子树 | 根节点 | 右子树 ] 。
(3)根据中序遍历中的左(右)子树的节点数量,可将前序遍历划分为 [ 根节点 | 左子树 | 右子树 ] 。

自此可确定 三个节点的关系 :1.树的根节点、2.左子树根节点、3.右子树根节点(即前序遍历中左(右)子树的首个元素)。

子树特点: 子树的前序和中序遍历仍符合以上特点,以题目示例的右子树为例:前序遍历:[20 | 15 | 7],中序遍历 [ 15 | 20 | 7 ] 。

根据子树特点,我们可以通过同样的方法对左(右)子树进行划分,每轮可确认三个节点的关系 。此递推性质让我们联想到用 递归方法 处理。

递归解析:

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

3 代码实现(Python3)

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        self.dic, self.po = {
     }, preorder
        for i in range(len(inorder)):
            self.dic[inorder[i]] = i
        return self.recur(0, 0, len(inorder) - 1)

    def recur(self, pre_root, in_left, in_right):
        if in_left > in_right: return # 终止条件:中序遍历为空
        root = TreeNode(self.po[pre_root]) # 建立当前子树的根节点
        i = self.dic[self.po[pre_root]]    # 搜索根节点在中序遍历中的索引,从而可对根节点、左子树、右子树完成划分。
        root.left = self.recur(pre_root + 1, in_left, i - 1) # 开启左子树的下层递归
        root.right = self.recur(i - in_left + pre_root + 1, i + 1, in_right) # 开启右子树的下层递归
        return root # 返回根节点,作为上层递归的左(右)子节点

4 复杂度分析

  • 时间复杂度 O(N) : N 为树的节点数量。初始化 HashMap 需遍历 inorder ,占用 O(N);递归共建立 N个节点,每层递归中的节点建立、搜索操作占用 O(1),因此递归占用 O(N) 。(最差情况为所有子树只有左节点,树退化为链表,此时递归深度 O(N) ;平均情况下递归深度 O(log_2 N)。
  • 空间复杂度 O(N): HashMap 使用 O(N) 额外空间;递归操作中系统需使用 O(N)额外空间。

深入了解复杂度:数据分析学习总结笔记11:空间复杂度和时间复杂度


  1. 解题思路_作者:jyd ↩︎

你可能感兴趣的:(数据结构,二叉树,算法,leetcode,面试)