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

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

大家好呀,我是帅蛋。


今天解决从前序与中序遍历构造二叉树,这种题目就是为了考察小婊贝们对二叉树前中后序遍历的掌握程度。


真正理解了它们的原理,解决起来是不难的。


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


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

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


那咱话不多说,开整!

640.png


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



题意


给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。


示例


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


提示


  • 1 <= len(preorder) == len(inorder) <= 3000
  • -3000 <= preorder[i],inorder[i] <= 3000
  • preorder 和 inorder 均无重复元素
  • inorder 均出现在 preorder
  • preorder 保证为二叉树的前序遍历序列
  • inorder 保证为二叉树的中序遍历序列


题目解析


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


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


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

640.png


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

640.png


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


知道了这些,解题思路呼之欲出,即:


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


然后分别对左右子树重复这个方式,直至结束。


图解


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


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

640.png


# 根节点的值为前序遍历的第一个元素值
rootVal = preorder[0]
# 创建根节点
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


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


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

640.png


# 找出前序遍历的左子树和右子树
# 前序遍历和中序遍历的左子树和右子树长度相等,所以可以通过中序遍历左右子树的长度来确定前序遍历左右子树的位置
preorderLeft = preorder[1:len(inorderLeft) + 1]
preorderRight = preorder[len(inorderLeft) + 1:]


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

640.png


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


本题解每个数组的元素遍历一次,时间复杂度为 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, preorder: List[int], inorder: List[int]) -> TreeNode:
        # 递归中止条件:树为空
        if not preorder or not inorder:
            return None
        # 根节点的值为前序遍历的第一个元素值
        rootVal = preorder[0]
        # 创建根节点
        root = TreeNode(rootVal)
        # 用根节点的值去中序数组中查找对应元素下标
        midIndex = inorder.index(rootVal)
        # 找出中序遍历的左子树和右子树
        # 左子树区间为 [0,midIndex),右子树区间为[midIndex+1,n-1]
        inorderLeft = inorder[:midIndex]
        inorderRight = inorder[midIndex + 1:]
        # 找出前序遍历的左子树和右子树
        # 前序遍历和中序遍历的左子树和右子树长度相等,所以可以通过中序遍历左右子树的长度来确定前序遍历左右子树的位置
        preorderLeft = preorder[1:len(inorderLeft) + 1]
        preorderRight = preorder[len(inorderLeft) + 1:]
        # 递归遍历左子树
        root.left = self.buildTree(preorderLeft, inorderLeft)
        # 递归遍历右子树
        root.right = self.buildTree(preorderRight, inorderRight)
        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[] preorder, int[] inorder) {
        // 递归中止条件:树为空
    if(preorder.length==0 || inorder.length==0) {
      return null;
    }
    // 根节点的值为前序遍历的第一个元素值
        // 创建根节点
    TreeNode root = new TreeNode(preorder[0]);
    for(int i=0 ;i<preorder.length; ++i) {
      //  # 用根节点的值去中序数组中查找对应元素下标
      if(preorder[0]==inorder[i]) {
                // 找出中序遍历的左子树和右子树
                int[] inorder_left = Arrays.copyOfRange(inorder,0,i);
        int[] inorder_right = Arrays.copyOfRange(inorder,i+1,inorder.length);
                // 找出前序遍历的左子树和右子树
        int[] preorder_left = Arrays.copyOfRange(preorder,1,i+1);
        int[] preorder_right = Arrays.copyOfRange(preorder,i+1,preorder.length);
                // 递归遍历左子树
        root.left = buildTree(preorder_left,inorder_left);
                // 递归遍历右子树
        root.right = buildTree(preorder_right,inorder_right);
        break;
      }
    }
    return root;
  }
}



图解从前序遍历与中序遍历构造二叉树到这就结束辣,是不是觉得还挺简单?


还是那句话,原理搞懂了,解题那是分分钟钟的事。


如果分分钟写不出来代码,那是编程能力的问题,得多想多练。


怎么练?可以先从给本蛋一键三连开始:点赞 + 在看 + 转发


我是帅蛋,下次见的时候你一定比现在进步了哟

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