面试经典150题[073]:从中序与后序遍历序列构造二叉树(LeetCode 106)

从中序与后序遍历序列构造二叉树(LeetCode 106)

题目链接:从中序与后序遍历序列构造二叉树(LeetCode 106)

难度:中等

1. 题目描述

给你两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历,postorder 是同一棵树的后序遍历,请你构造并返回这颗二叉树。

要求:

  • 树中没有重复元素
  • 数组长度 1 <= n <= 3000
  • -3000 <= inorder[i], postorder[i] <= 3000
  • inorder 和 postorder 都由唯一值组成,且 postorder 中的每个值都在 inorder 中出现
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历

示例:

复制代码
输入: inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出: [3,9,20,null,null,15,7]
解释: 二叉树结构为根节点3,左子节点9,右子节点20(左15,右7)。

输入: inorder = [2,1], postorder = [2,1]
输出: [1,2]
解释: 根节点1,左子节点2。

2. 问题分析

2.1 规律

  • 中序遍历:左子树 -> 根 -> 右子树。
  • 后序遍历:左子树 -> 右子树 -> 根。
  • 后序遍历的最后一个元素总是当前子树的根节点。
  • 在中序遍历中找到根节点的位置,其左侧是左子树的中序,右侧是右子树的中序。
  • 由于值唯一,可用哈希表存储中序值到索引的映射,避免每次线性搜索。
  • 核心问题:如何递归划分左右子树,并高效构建树结构?

2.2 递归思路

我们使用递归算法:

  • 预构建哈希表 inorder_map,存储每个值在中序数组中的索引。
  • 使用后序数组从末尾开始弹出根节点(因为后序最后是根)。
  • 递归函数 helper(in_start, in_end)
    • 如果 in_start > in_end,返回 None(空树)。
    • 取当前根值 root_val = postorder[post_idx]post_idx -= 1
    • 创建根节点。
    • 找到根在中序的索引 idx = inorder_map[root_val]
    • 先递归构建右子树 root.right = helper(idx + 1, in_end)(因为后序中右子树在左子树之后,从后往前先遇到右子树)。
    • 再递归构建左子树 root.left = helper(in_start, idx - 1)
  • 初始调用:helper(0, n-1)post_idx 初始化为 n-1

3. 代码实现

Python

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, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
        n = len(inorder)
        if n == 0:
            return None
        
        inorder_map = {val: idx for idx, val in enumerate(inorder)}
        post_idx = n - 1
        
        def helper(in_start: int, in_end: int) -> Optional[TreeNode]:
            nonlocal post_idx
            if in_start > in_end:
                return None
            
            root_val = postorder[post_idx]
            post_idx -= 1
            root = TreeNode(root_val)
            idx = inorder_map[root_val]
            
            root.right = helper(idx + 1, in_end)
            root.left = helper(in_start, idx - 1)
            
            return root
        
        return helper(0, n - 1)

C++

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int n = inorder.size();
        if (n == 0) return nullptr;
        
        unordered_map<int, int> inorder_map;
        for (int i = 0; i < n; ++i) {
            inorder_map[inorder[i]] = i;
        }
        
        int post_idx = n - 1;
        
        function<TreeNode*(int, int)> helper = [&](int in_start, int in_end) -> TreeNode* {
            if (in_start > in_end) return nullptr;
            
            int root_val = postorder[post_idx];
            --post_idx;
            TreeNode* root = new TreeNode(root_val);
            int idx = inorder_map[root_val];
            
            root->right = helper(idx + 1, in_end);
            root->left = helper(in_start, idx - 1);
            
            return root;
        };
        
        return helper(0, n - 1);
    }
};

4. 复杂度分析

  • 时间复杂度:O(n),构建哈希表 O(n),递归遍历每个节点一次 O(n)。
  • 空间复杂度:O(n),哈希表 O(n),递归栈最坏 O(n)(链状树)。

5. 总结

  • 树重建问题 + 中序+后序 → 递归划分子树是首选。
  • 核心维护后序索引和中序映射,很通用。
    • 类似从中序+前序构建树(LeetCode 105),但调整构建顺序(前序先左后右,后序先右后左)。
    • 可扩展到其他遍历组合或序列化问题。

复习

面试经典150题[013]:除自身以外数组的乘积(LeetCode 238)

面试经典150题[043]:字母异位词分组(LeetCode 49)

面试经典150题[058]:两数相加(LeetCode 2)

相关推荐
业精于勤的牙2 小时前
浅谈:算法中的斐波那契数(五)
算法·leetcode·职场和发展
regon2 小时前
第九章 述职11 交叉面试
面试·职场和发展·《打造卓越团队》
LYFlied2 小时前
【每日算法】LeetCode 105. 从前序与中序遍历序列构造二叉树
数据结构·算法·leetcode·面试·职场和发展
DanyHope2 小时前
LeetCode 206. 反转链表:迭代 + 递归双解法全解析
算法·leetcode·链表·递归·迭代
DanyHope2 小时前
LeetCode 两数之和:从 O (n²) 到 O (n),空间换时间的经典实践
前端·javascript·算法·leetcode·职场和发展
1张驰咨询13 小时前
智慧城市交付困局:用六西格玛培训,将项目毛利从行业平均的12%提升至龙头水平的22%
人工智能·职场和发展·智慧城市·六西格玛
DanyHope3 小时前
LeetCode 283. 移动零:双指针双解法(原地交换 + 覆盖补零)全解析
数据结构·算法·leetcode
gis分享者3 小时前
如何在 Shell 脚本中如何使用条件判断语句?(中等)
面试·shell·脚本·语法·使用·判断·条件
LYFlied3 小时前
【每日算法】LeetCode 114. 二叉树展开为链表:从树结构到线性结构的优雅转换
数据结构·算法·leetcode·链表·面试·职场和发展