ACM 选手图解 LeetCode 从中序与后序遍历构造二叉树

简介: ACM 选手图解 LeetCode 从中序与后序遍历构造二叉树

大家好呀,我是快乐的蛋蛋。


今天解决从中序与后序遍历序列构造二叉树,和之前的【从前序与中序遍历构造二叉树】相同,考察小婊贝们对二叉树前中后序遍历的掌握程度。


关于二叉树的前中后序遍历,如果还不太了解,可以看下面这两篇文章:


ACM 选手带你玩转二叉树前中后序遍历(递归版)

ACM 选手带你玩转二叉树前中后序遍历(非递归版)


还有,提醒一下,一定要看文末呦。

640.png


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


题意


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


示例


输入:inorder = [9,3,15,20,7],postorder = [9,15,7,20,3]。
输出:[3,9,20,null,null,15,7]


提示


  • 假设树中没有重复元素。


题目解析


难度中等,考察二叉树前中后序遍历掌握程度的经典好题


同样对于这道题,如果真的完全理解了前中后序遍历的原理,是不难的。


二叉树的中序遍历顺序为:左子树、根、右子树,所以对于二叉树来说,中序遍历的形式如下图所示:

640.png


二叉树的后序遍历顺序为:左子树、右子树、根,所以对于二叉树来说,后序遍历的形式如下图所示:

640.png


通过上面两个图,就可以很清晰的看出,中序遍历的根节点左侧是左子树,右侧是右子树,后序遍历的最后一个元素为根节点


看懂了这个,那解题步骤也就出来了:


在中序遍历中找到根节点,从而找到左右子树,知道左右子树的范围,从而后序遍历中的左右子树也就确定好了。


然后分别对左右子树用同样的方式递归构造下去。


图解


inorder = [9,3,15,20,7],postorder = [9,15,7,20,3] 为例。


第 1 步,在后序遍历中找到根节点的值,然后创建根节点。

640.png

# 根节点的值为后序遍历的最后一个元素值
rootVal = postorder[-1]
# 创建根节点
root = TreeNode(rootVal)

第 2 步,在中序遍历中找到根节点的位置 midIndex。

640.png


# 用根节点的值去中序数组中查找对应元素下标
midIndex = inorder.index(rootVal)


如上图所示,中序遍历的根节点下标位置为 1。


第 3 步,找出中序遍历的左右子树。


可以很明显的从上图中看出:


  • 中序遍历的左子树范围为[0,midIndex - 1]
  • 中序遍历的右子树的范围为[midIndex + 1,n - 1]


640.png


# 找出中序遍历的左子树和右子树
# 左子树区间为 [0,midIndex),右子树区间为[midIndex+1,n-1]
inorderLeft = inorder[:midIndex]
inorderRight = inorder[midIndex + 1:]


第 4 步,找出后序遍历的左右子树。


通过第 3 步得出,中序遍历左子树长度为 1,右子树长度为 3,所以对于后序遍历来说,其左子树长度同样为 1,右子树长度同样为 3


这样就可以在后序遍历中确定其左右子树的范围:


  • 后序遍历左子树的范围为 [0,len(inorderLeft))
  • 后序遍历右子树的范围为 [len(inorderLeft), n - 1)

640.png



# 找出后序遍历的左子树和右子树
# 后序遍历和中序遍历的左子树和右子树长度相等,所以可以通过中序遍历左右子树的长度来确定后序遍历左右子树的位置
postorderLeft = postorder[: len(inorderLeft)]
postorderRight = postorder[len(inorderLeft): len(inorder) - 1]


接下来,以同样的方式递归遍历左子树和右子树,直至结束。

640.png


# 递归遍历左子树
root.left = self.buildTree(inorderLeft, postorderLeft)
# 递归遍历右子树
root.right = self.buildTree(inorderRight, postorderRight)

本题解每个数组的元素遍历一次,时间复杂度为 O(n)


此外结果输出一个数组,空间复杂度 O(n)


代码实现


Python 代码实现


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        # 递归中止条件:树为空
        if not inorder or not postorder:
            return None
        # 根节点的值为后序遍历的最后一个元素值
        rootVal = postorder[-1]
        # 创建根节点
        root = TreeNode(rootVal)
        # 用根节点的值去中序数组中查找对应元素下标
        midIndex = inorder.index(rootVal)
        # 找出中序遍历的左子树和右子树
        # 左子树区间为 [0,midIndex),右子树区间为[midIndex+1,n-1]
        inorderLeft = inorder[:midIndex]
        inorderRight = inorder[midIndex + 1:]
        # 找出后序遍历的左子树和右子树
        # 后序遍历和中序遍历的左子树和右子树长度相等,所以可以通过中序遍历左右子树的长度来确定后序遍历左右子树的位置
        postorderLeft = postorder[: len(inorderLeft)]
        postorderRight = postorder[len(inorderLeft): len(inorder) - 1]
        # 递归遍历左子树
        root.left = self.buildTree(inorderLeft, postorderLeft)
        # 递归遍历右子树
        root.right = self.buildTree(inorderRight, postorderRight)
        return root


Java 代码实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
  public TreeNode buildTree(int[] inorder, int[] postorder) {
        // 递归中止条件:树为空
    if(inorder.length == 0 || postorder.length == 0) {
      return null;
    }
    // 根节点的值为前序遍历的第一个元素值
    // 创建根节点
    TreeNode root = new TreeNode(postorder[postorder.length - 1]);
    for(int i=0 ;i<inorder.length; ++i) {
      //  # 用根节点的值去中序数组中查找对应元素下标
      if(postorder[postorder.length - 1] == inorder[i]) {
        // 找出中序遍历的左子树和右子树
        int[] inorder_left = Arrays.copyOfRange(inorder,0,i);
        int[] inorder_right = Arrays.copyOfRange(inorder,i+1,inorder.length);
        // 找出后序遍历的左子树和右子树
        int[] postorder_left = Arrays.copyOfRange(postorder,0,i);
        int[] postorder_right = Arrays.copyOfRange(postorder,i,postorder.length - 1);
        // 递归遍历左子树
        root.left = buildTree(inorder_left,postorder_left);
        // 递归遍历右子树
        root.right = buildTree(inorder_right,postorder_right);
        break;
      }
    }
    return root;
  }
}


图解从中序与后序遍历序列构造二叉树到这就结束辣,如果你【从前序与中序遍历构造二叉树】有认真做。就会发现,这两道题差不多,只是稍微做了些改变。


聪明的你可能猜出下一道题该是:从前序与后序遍历序列构造二叉树。


哈哈哈哈...可惜猜错了:前序与后序遍历序列无法确定一棵二叉树

640.jpg

原因很简单。


因为前序遍历的根节点在最前面,后序遍历的根节点在最后面,根节点倒是好确定,但是左右子树就不知道谁是谁了。


这一点希望大家记住。


当然还得记住本帅蛋的一键三连,毕竟点赞 + 在看 + 转发的小婊贝都是真爱。


我是帅蛋,下次让我见到更优秀的你呦~


相关文章
|
1月前
【LeetCode 43】236.二叉树的最近公共祖先
【LeetCode 43】236.二叉树的最近公共祖先
18 0
|
1月前
【LeetCode 38】617.合并二叉树
【LeetCode 38】617.合并二叉树
14 0
|
1月前
【LeetCode 37】106.从中序与后序遍历构造二叉树
【LeetCode 37】106.从中序与后序遍历构造二叉树
16 0
|
1月前
【LeetCode 34】257.二叉树的所有路径
【LeetCode 34】257.二叉树的所有路径
12 0
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
56 6
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
113 2
|
18天前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
16 1
|
2月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
3月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
56 7