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;
  }
}



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


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


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


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


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

相关文章
|
2天前
leetcode代码记录(二叉树的所有路径
leetcode代码记录(二叉树的所有路径
8 0
|
3天前
leetcode代码记录(对称二叉树 中序遍历+回文串 为什么不行
leetcode代码记录(对称二叉树 中序遍历+回文串 为什么不行
7 0
|
3天前
leetcode代码记录(二叉树的最小深度
leetcode代码记录(二叉树的最小深度
7 0
|
3天前
leetcode代码记录(二叉树的最大深度
leetcode代码记录(二叉树的最大深度
6 0
|
3天前
leetcode代码记录(翻转二叉树
leetcode代码记录(翻转二叉树
5 0
|
3天前
|
算法 C++
【刷题】Leetcode 1609.奇偶树
这道题是我目前做过最难的题,虽然没有一遍做出来,但是参考大佬的代码,慢慢啃的感觉的真的很好。刷题继续!!!!!!
6 0
|
3天前
|
算法 索引
【刷题】滑动窗口精通 — Leetcode 30. 串联所有单词的子串 | Leetcode 76. 最小覆盖子串
经过这两道题目的书写,相信大家一定深刻认识到了滑动窗口的使用方法!!! 下面请大家继续刷题吧!!!
8 0
|
3天前
|
算法
【刷题】 leetcode 面试题 08.05.递归乘法
递归算法是一种在计算机科学和数学中广泛应用的解决问题的方法,其基本思想是利用问题的自我相似性,即将一个大问题分解为一个或多个相同或相似的小问题来解决。递归算法的核心在于函数(或过程)能够直接或间接地调用自身来求解问题的不同部分,直到达到基本情况(也称为基础案例或终止条件),这时可以直接得出答案而不必再进行递归调用。
21 4
【刷题】 leetcode 面试题 08.05.递归乘法
|
3天前
|
存储 算法 安全
【刷题】 leetcode 面试题 01.06 字符串压缩
来看效果: 非常好!!!过啦!!!
25 5
【刷题】 leetcode 面试题 01.06 字符串压缩

热门文章

最新文章