LeetCode 105. 从前序与中序遍历序列构造二叉树:分治递归思路详解

LeetCode 105. 从前序与中序遍历序列构造二叉树:分治递归思路详解

前言

LeetCode 105 是二叉树构造题中的经典代表,也是面试高频题。

很多人知道:

前序可以找到根节点,中序可以划分左右子树。

但是写代码时,经常会卡在:

  • 左子树区间怎么算?
  • 右子树区间怎么算?
  • 为什么需要哈希表?

这篇文章就把整个递归过程一步一步讲清楚。


一、题目描述

给定:

  • 前序遍历数组 preorder
  • 中序遍历数组 inorder

要求恢复原来的二叉树。

例如:

text 复制代码
preorder

3 9 20 15 7

inorder

9 3 15 20 7

恢复后的树:

text 复制代码
      3
     / \
    9   20
       /  \
      15   7

二、先理解两种遍历的特点

前序遍历

顺序:

复制代码
根
左
右

因此:

数组第一个元素一定是当前子树的根节点。

例如:

复制代码
3 9 20 15 7

根就是:

复制代码
3

中序遍历

顺序:

复制代码
左
根
右

例如:

复制代码
9 3 15 20 7

找到根:

复制代码
3

左边:

复制代码
9

全部属于左子树。

右边:

复制代码
15 20 7

全部属于右子树。

所以:

中序遍历负责:

划分左右子树。


三、为什么能够递归?

恢复整棵树,其实就是不断重复下面三件事:

第一步:

前序第一个元素找根。

第二步:

中序找到根的位置。

第三步:

递归恢复左右子树。

整个过程完全一致。

所以天然适合递归。


四、如何切分区间?

这是整道题最关键的地方。

例如:

复制代码
preorder

3 9 20 15 7

inorder

9 3 15 20 7

根:

复制代码
3

在中序的位置:

复制代码
9 | 3 | 15 20 7

左子树节点数量:

复制代码
1

因此:

前序中:

复制代码
根 左子树 右子树

3 | 9 | 20 15 7

左子树也只有一个节点。

所以:

左子树区间就确定了。

右子树自然也就确定了。

整个递归都是利用:

左子树节点数量。

来不断切分前序数组。


五、为什么使用哈希表?

如果每次都去中序数组查找根节点:

python 复制代码
inorder.index(root)

时间复杂度:

复制代码
O(n)

递归过程中会查很多次。

整体退化成:

复制代码
O(n²)

因此提前建立:

python 复制代码
value -> index

哈希表。

例如:

python 复制代码
{
9:0,
3:1,
15:2,
20:3,
7:4
}

以后查找:

复制代码
O(1)

整体复杂度降低到:

复制代码
O(n)

六、递归流程

整个递归可以概括为四步。

第一步

前序第一个元素作为当前根节点。


第二步

通过哈希表找到根节点在中序中的位置。


第三步

根据左子树节点数量切分前序区间。


第四步

递归构造左右子树。

直到区间为空。

返回 None。


七、完整代码

python 复制代码
class Solution:
    def buildTree(self, preorder, inorder):

        index = {value: i for i, value in enumerate(inorder)}

        def build(pre_left, pre_right, in_left, in_right):

            if pre_left > pre_right:
                return None

            root_val = preorder[pre_left]

            root = TreeNode(root_val)

            mid = index[root_val]

            left_size = mid - in_left

            root.left = build(
                pre_left + 1,
                pre_left + left_size,
                in_left,
                mid - 1
            )

            root.right = build(
                pre_left + left_size + 1,
                pre_right,
                mid + 1,
                in_right
            )

            return root

        return build(
            0,
            len(preorder) - 1,
            0,
            len(inorder) - 1
        )

八、复杂度分析

时间复杂度

复制代码
O(n)

每个节点只访问一次。


空间复杂度

复制代码
O(n)

主要来自:

  • 哈希表
  • 递归栈

九、高频易错点

1、没有建立哈希表

直接使用:

python 复制代码
index()

会导致:

复制代码
O(n²)

2、左右区间切分错误

真正决定切分的是:

复制代码
左子树节点数量

而不是:

根节点的位置。


3、遗漏终止条件

必须写:

python 复制代码
if pre_left > pre_right:
    return None

否则容易数组越界。


4、不断切片数组

例如:

python 复制代码
preorder[:k]

虽然能写出来。

但是每次都会复制数组。

空间和时间都会增加。

面试中更推荐:

使用左右边界表示区间。


十、总结

这道题其实就是不断重复三个动作:

  1. 前序找到根节点。
  2. 中序划分左右子树。
  3. 根据左子树节点数量切分前序区间,递归继续构造。

掌握了这种「前序确定根、中序划分左右、递归分治」的思路,不仅能解决 LeetCode 105,还能应对很多二叉树重建类题目,是二叉树递归中的经典模板。

相关推荐
豆瓣鸡5 分钟前
算法日记 - Day10
算法
戴西软件15 分钟前
戴西CAxWorks.VPG车辆工程仿真软件技术解析(上)——安全仿真体系的自动化构建
运维·网络·数据库·人工智能·算法·安全·自动化
zander25825 分钟前
4. 寻找两个正序数组的中位数:用分割点代替合并
数据结构·算法
Kisorge36 分钟前
【电机控制器】 基于STSPIN32G4的FOC控制
stm32·嵌入式硬件·算法
Elivs1 小时前
RMSNorm函数
人工智能·算法·机器学习
tudousisi2221 小时前
P4447 [AHOI2018初中组] 分组 题解复盘
算法
SNAKEpc121381 小时前
OpenGL(十一)- 变换管线
c语言·c++·算法·矩阵·图形渲染
饼饼学习空间智能3 小时前
遥操作数据能否提升机器人自主化水平?详解模仿学习、仿真放大与Sim2Real闭环
人工智能·算法
间歇性努力持续性发呆的野生快乐选手4 小时前
dfs回溯,bfs枚举,完全背包
c++·算法·深度优先·宽度优先
嵌入式冰箱4 小时前
2026年华数杯B题完整解题思路与建模方案
算法