ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

力扣刷题#34-0105-从前序与中序遍历序列构造二叉树

力扣刷题#34-0105-从前序与中序遍历序列构造二叉树

力扣刷题#34-0105-从前序与中序遍历序列构造二叉树

题目

给定两个整数数组 preorderinorder,其中 preorder 是二叉树的前序遍历,inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]3/ \9  20/ \15  7

我的思路

核心洞察:两种遍历各自给什么

遍历 顺序 我们能得到什么
前序 中 → 左 → 右 第一个元素一定是根
中序 左 → 中 → 右 根的左边是左子树,右边是右子树

两个信息一组合:

preorder = [3, 9, 20, 15, 7]    → 3 是根
inorder  = [9, 3, 15, 20, 7]    → 9 是左子树,15,20,7 是右子树然后递归地处理"左子树"和"右子树",一切重来

生活化类比:拼图游戏。手里有"前序卡片"和"中序卡片"各一叠。每次拿前序第一张当根,在中序堆里找到这张卡——左边一叠是左子树,右边一叠是右子树。递归地处理每一小叠。


我的 AC 代码(含注释)

class Solution {
public:// pos: 值 → 在 inorder 中的下标,O(1) 查找根的位置unordered_map<int, int> pos;// 用 [prel, prer] × [inl, inr] 描述当前要构建的子树区间TreeNode* build(vector<int>& preorder, vector<int>& inorder,int prel, int prer, int inl, int inr) {// ① 出口:前序区间空了(起点超过终点)→ 没有节点if (prel > prer) return nullptr;// ② 前序第一个元素 = 当前子树根TreeNode* root = new TreeNode(preorder[prel]);// ③ 在中序里找根的位置,计算左子树长度int rootpos = pos[root->val];   // 根在中序的下标int leftsize = rootpos - inl;   // 根左边有几个元素 = 左子树长度// ④ 递归左子树://    preorder: 跳过根(prel+1),占 leftsize 个位置//    inorder:  从 inl 到 rootpos-1(根左边)root->left = build(preorder, inorder,prel + 1, prel + leftsize, inl, rootpos - 1);// ⑤ 递归右子树://    preorder: 跳过根+左子树,到区间末尾//    inorder:  从 rootpos+1(根右边)到 inrroot->right = build(preorder, inorder,prel + leftsize + 1, prer, rootpos + 1, inr);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {// ⑥ 预处理:建立值 → 中序下标 的哈希表for (int i = 0; i < inorder.size(); i++)pos[inorder[i]] = i;// ⑦ 从完整区间开始递归return build(preorder, inorder, 0, preorder.size() - 1, 0,inorder.size() - 1);}
};

逐步拆解(用例子走一遍)

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

第一层:build(0, 4, 0, 4)

步骤 计算 结果
preorder[0] 3
根在中序位置 pos[3] 1
leftsize 1 - 0 1
左子树 build(1, 1, 0, 0) preorder[1..1]=[9]
右子树 build(2, 4, 2, 4) preorder[2..4]=[20,15,7]

第二层:build(1, 1, 0, 0) 和 build(2, 4, 2, 4)

  • build(1,1,0,0):根=9,leftsize=0,左右都是空区间 → 叶子
  • build(2,4,2,4):根=20(preorder[2]),pos[20]=3,leftsize=3-2=1
    • 左子树 build(2,2,2,2) → preorder[2..2]=[15]
    • 右子树 build(4,4,4,4) → preorder[4..4]=[7]

递归树全貌

build(0,4,0,4) → root=3
├── build(1,1,0,0) → root=9(叶子)
└── build(2,4,2,4) → root=20├── build(2,2,2,2) → root=15(叶子)└── build(4,4,4,4) → root=7(叶子)

为什么需要四个边界参数?

每次递归处理"两段子数组"(preorder 一段 + inorder 一段),各自需要起点和终点:

参数 含义
prel 当前子树在 preorder 中的起点
prer 当前子树在 preorder 中的终点
inl 当前子树在 inorder 中的起点
inr 当前子树在 inorder 中的终点

递归 = 不断把这两个区间切小,直到空。


为什么用哈希表?

如果每次都在 inorder 里 for 扫描找根的位置,每层 O(n),总共 O(n²)。用哈希表把查找降到 O(1),总时间 O(n)。

这是"空间换时间"的典型应用——先用 O(n) 空间建表,换 O(n) 的总时间。


复杂度分析

维度 说明
时间复杂度 O(n) 每个节点构建一次,哈希查找 O(1)
空间复杂度 O(n) 哈希表 O(n) + 递归栈 O(h)

关键点总结

关键点 说明
前序的作用 第一个元素 = 根
中序的作用 根的左边 = 左子树,右边 = 右子树
leftsize 根在中序的位置 - inl = 左子树长度
左子树前序 [prel+1, prel+leftsize]
右子树前序 [prel+leftsize+1, prer]
哈希表 值→中序下标,O(1) 查找

本文档由 AI 辅助生成,作者提供问题,思路和代码,AI仅负责文本修饰,综合获得以上内容。

返回列表