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月前
|
存储 算法
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
文章深入探讨了二叉树的层序遍历方法,并展示了如何通过队列实现层序遍历的算法逻辑,同时指出掌握层序遍历技巧可以帮助解决LeetCode上的多道相关题目。
二叉树进阶-学会层序遍历助你一次刷完leetcode10道题
|
1月前
|
算法 Java
LeetCode第94题二叉树的中序遍历
文章介绍了LeetCode第94题"二叉树的中序遍历"的解法,使用递归实现了中序遍历的过程,遵循了"左根右"的遍历顺序,并提供了清晰的Java代码实现。
LeetCode第94题二叉树的中序遍历
|
1月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
36 7
|
1月前
|
存储 算法 Java
LeetCode经典算法题:二叉树遍历(递归遍历+迭代遍历+层序遍历)以及线索二叉树java详解
LeetCode经典算法题:二叉树遍历(递归遍历+迭代遍历+层序遍历)以及线索二叉树java详解
60 0
|
1月前
|
算法 Java
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
32 0
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
41 6
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
35 4
|
1月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
73 2
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
17 4
|
1月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
40 5