Leetcode105-前序中序来构造二叉树-Python-分解问题的思路

这是一个非常经典的二叉树构建问题(LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal)。

你的代码采用了DFS(深度优先搜索)+ 哈希表优化 的策略,逻辑非常清晰。不过,我注意到代码中构建哈希表的地方有一个小小的逻辑反转 需要修正(k:v 应该是 v:k,因为我们需要通过"值"查"下标")。

以下是为你生成的博客文章,我已经修正了那个小瑕疵,并添加了图解说明:


算法图解:如何从前序与中序遍历"复原"二叉树

在二叉树的算法题中,**"构造二叉树"**系列绝对是重难点。今天我们来拆解 LeetCode 105 题:给定两个数组 preorder(前序遍历)和 inorder(中序遍历),如何通过递归的方式完美复原这棵树?

核心思路:分而治之

解决这个问题的关键在于理解两种遍历方式的排布规律

  1. 前序遍历 (Preorder)[ 根节点 | 左子树所有节点 | 右子树所有节点 ]

    • 特性 :数组的第一个元素 永远是当前的根节点
  2. 中序遍历 (Inorder)[ 左子树所有节点 | 根节点 | 右子树所有节点 ]

    • 特性:一旦我们找到了根节点的位置,它左边的就是左子树,右边的就是右子树。

破局步骤

我们的策略可以总结为 "定位根 -> 切分左右 -> 递归构建"

  1. preorder 的第一个元素拿到 Root

  2. inorder 中找到这个 Root 的下标 (记为 rootIdx)。

  3. 通过 rootIdx,计算出左子树的长度 (leftLen)。

  4. 利用 leftLen,我们将 preorderinorder 两个数组精确地"切"成两半,分别丢给递归函数去构建左孩子和右孩子。


逻辑图解

假设:

  • preorder = [3, 9, 20, 15, 7]

  • inorder = [9, 3, 15, 20, 7]

第一轮递归:

  1. 找根preorder[0]3

  2. 定位 :在 inorder 中找到 3 的位置,下标是 1

  3. 切分

    • 左子树 :长度为 1(即 9)。

    • 右子树 :剩下的部分(即 15, 20, 7)。

此时我们可以确定根节点 3 的左右边界,然后递归下去。


代码实现 (Python)

为了避免每次都在 inorder 中遍历寻找根节点(那样会导致时间复杂度变成 O(N\^2)),我们使用一个 哈希表 (Hash Map) 预先存储 值 -> 下标 的映射,实现 O(1) 的快速查找。

Python

复制代码
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        # ⚡️ 优化点:构建哈希表,通过"数值"快速查到"中序遍历中的下标"
        # 注意:这里是 val : index
        idxInorder = {val: i for i, val in enumerate(inorder)}
        
        # 定义 DFS 函数
        # pStart, pEnd: 前序遍历的范围
        # iStart, iEnd: 中序遍历的范围
        def dfs(pStart, pEnd, iStart, iEnd):
            # 🛑 终止条件:范围无效(比如 pStart > pEnd),说明子树为空
            if pStart > pEnd or iStart > iEnd:
                return None
            
            # 1. 建立根节点
            root_val = preorder[pStart]
            root = TreeNode(root_val)
            
            # 2. 在中序遍历中找到根节点的位置
            rootIdx = idxInorder[root_val]
            
            # 3. 计算左子树的节点数量 (关键步骤!)
            leftLen = rootIdx - iStart
            
            # 4. 递归构建左右子树
            # 左子树:
            # Preorder范围: [pStart+1, pStart+leftLen]
            # Inorder范围:  [iStart, rootIdx-1]
            root.left = dfs(pStart + 1, pStart + leftLen, iStart, rootIdx - 1)
            
            # 右子树:
            # Preorder范围: [pStart+leftLen+1, pEnd]
            # Inorder范围:  [rootIdx+1, iEnd]
            root.right = dfs(pStart + leftLen + 1, pEnd, rootIdx + 1, iEnd)
            
            return root
            
        return dfs(0, len(preorder)-1, 0, len(inorder)-1)

难点解析:索引计算

代码中最容易晕的地方就是递归时的索引范围,一定要死记这个公式:

  • 左子树长度 (leftLen) = rootIdx (根在中序的位置) - iStart (中序的起始位置)。

  • 左孩子的前序范围 :从 pStart + 1 开始,长度为 leftLen,所以结束于 pStart + leftLen

  • 右孩子的前序范围 :紧接着左孩子之后,即 pStart + leftLen + 1 开始,直到 pEnd


复杂度分析

  • 时间复杂度O(N)

    • 我们需要遍历所有节点一次来构建树。

    • 哈希表的查找是 O(1) 的,构建哈希表也是 O(N)

  • 空间复杂度O(N)

    • 哈希表占用了 O(N) 的空间。

    • 递归调用栈在最坏情况(链状树)下也需要 O(N) 的空间。

总结

这道题是理解 分治算法 (Divide and Conquer) 在树结构中应用的绝佳案例。

只要记住 "前序定根,中序定界" 这八字口诀,配合哈希表提速,就能轻松拿下!

希望这篇解析能帮你彻底搞懂二叉树的构建!

相关推荐
SelectDB6 小时前
Apache Doris Python UDF:让 SQL 直接调用 Python 生态,支撑 Agent 时代复杂业务逻辑
大数据·数据库·python
荣码13 小时前
GraphRAG:普通RAG只能回答"点"的问题,我踩了4个坑才搞懂
java·python
金銀銅鐵1 天前
[Python] 基于欧几里得算法,实现分数约分计算器
python·数学
Lyn_Li1 天前
Kaggle Top 5 | 198只股票、200条数据的金融预测——BattleFin高分方案从零复现
python·kaggle·比赛复盘·金融预测
小九九的爸爸1 天前
前端想要入门Agent开发,要具备哪些Python基础?
python·agent·ai编程
阿耶同学1 天前
手把手教你用 LangGraph 搭建三层嵌套 Agent 架构
python·程序员
花酒锄作田2 天前
Pydantic校验配置文件
python
hboot2 天前
AI工程师第四课 - 深度学习入门
pytorch·python·神经网络
ZhengEnCi2 天前
P2M-Matplotlib折线图完全指南-从数据可视化到趋势分析的Python绘图利器
python·matlab·数据可视化
ZhengEnCi3 天前
P2L-Matplotlib饼图完全指南-从数据可视化到图表定制的Python绘图利器
python·matlab