BM40 重建二叉树¶
中等通过率 28.07%python3核心代码模式样例通过牛客 AC数组树dfs
讲解章节:树的基础与遍历
一句话
前序给根、中序切左右,用哈希表定位根下标后按区间迭代地拼。
解题思路¶
这题考什么¶
单看任何一个遍历序列都还原不出树,两个序列各提供一半信息,合起来才唯一 确定一棵树:前序告诉你谁是根,中序告诉你根把哪些节点分到了左右两边。
前序 [1,2,4,7,3,5,6,8] 第一个元素一定是根
中序 [4,7,2,1,5,3,8,6] 根把序列切成 [左子树][根][右子树]
根 = 1,它在中序里的下标是 3 -> 左子树 3 个节点、右子树 4 个节点
知道左子树大小,前序随即也能切:
[1] [2,4,7] [3,5,6,8]
根 左 3 个 右 4 个
对两段重复同样的切分,整棵树就拼出来了。
两个实现要点:
- 哈希表定位根。每层都在中序里线性扫一遍找根是 O(n^2), 预先建「值 -> 中序下标」的字典,每层查根降到 O(1),总复杂度 O(n)。 题面保证两个序列都无重复元素,值才可以直接当键。
- 写成迭代。栈里存「已经建好的节点 + 它负责的前序区间 + 中序区间」, 弹出时用左子树大小把两个区间同步切开: 左子树: 前序 [pl+1, pl+left] 中序 [il, k-1] 右子树: 前序 [pl+left+1, pr] 中序 [k+1, ir] 孩子非空就先按对应前序段的首元素 new 出来、挂到父亲身上, 再连同区间一起入栈——出栈时父子关系已经接好,不必回填。
数据规模与复杂度¶
n <= 2000,节点值满足 -10000 <= val <= 10000 且无重复;时限「其他语言 2 秒」。 每个节点建一次、入栈一次,查根 O(1),时间 O(n); 空间 O(n):下标字典 n 项,栈深等于树高。
坑在哪¶
- 左子树大小要用中序下标算:left = k - il,是「中序左段的长度」。 直接拿 k 当长度只在 il == 0 时碰巧对,进入子区间后全线错位。
- 两个区间必须同步切分。前序段和中序段描述的是同一批节点, 只要长度对上就一定对应;把前序的左段长度写成 k - il 之外的任何值, 左右子树会互相偷节点,树形彻底乱掉。
- 右子树是否为空用中序侧的 ir - k > 0 判断,与左侧的 k - il 对称, 不必再去前序区间上另算一遍长度。
- 哈希表不只是优化。每层线性找根在链状树上是 O(n^2):本题 n <= 2000 还能扛(约 4e6 次比较),但同一套代码在 BM41 那里 n 到 1e4, O(n^2) 就是 1e8 次比较,Python 上必然超时。
- n <= 2000 已经超过 CPython 默认的 1000 层递归上限,链状树(前序与中序 互为逆序时就是一条链)的递归深度就是 n,递归版会 RecursionError。
- 空数组返回 None。少了这道守卫,preOrder[0] 直接 IndexError。
- 值可以是负数(下界 -10000),所以任何「用 0 或 -1 表示空节点」的写法 在这里都不成立,判空一律用 is None。
参考实现¶
[:octicons-arrow-left-16: BM39](BM39.md) [BM41 :octicons-arrow-right-16:](BM41.md)