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) 在树结构中应用的绝佳案例。

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

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

相关推荐
开源技术1 小时前
深入了解Turso,这个“用Rust重写的SQLite”
人工智能·python
初恋叫萱萱1 小时前
构建高性能生成式AI应用:基于Rust Axum与蓝耘DeepSeek-V3.2大模型服务的全栈开发实战
开发语言·人工智能·rust
cyforkk2 小时前
12、Java 基础硬核复习:集合框架(数据容器)的核心逻辑与面试考点
java·开发语言·面试
u0109272712 小时前
RESTful API设计最佳实践(Python版)
jvm·数据库·python
我材不敲代码6 小时前
Python实现打包贪吃蛇游戏
开发语言·python·游戏
身如柳絮随风扬7 小时前
Java中的CAS机制详解
java·开发语言
0思必得08 小时前
[Web自动化] Selenium处理动态网页
前端·爬虫·python·selenium·自动化
韩立学长8 小时前
【开题答辩实录分享】以《基于Python的大学超市仓储信息管理系统的设计与实现》为例进行选题答辩实录分享
开发语言·python
qq_192779878 小时前
高级爬虫技巧:处理JavaScript渲染(Selenium)
jvm·数据库·python
u0109272719 小时前
使用Plotly创建交互式图表
jvm·数据库·python