LC105 从前序与中序遍历序列构造二叉树¶
中等通过率 73.3%python3核心代码模式二叉树样例通过牛客 AC树数组哈希表分治二叉树
讲解章节:树的基础与遍历
一句话
前序给出根,中序按根切成左右两段,逐层分解。
解题思路¶
这题考什么¶
两条序列各自都不足以确定一棵树,合起来才行(前提是元素互不重复,题面已保证):
由此得到唯一的分解路径:前序的第一个元素必是当前子树的根; 到中序里找到这个值的位置 mid,它左边的一段就是左子树的全部节点, 右边的一段就是右子树的全部节点。两段的长度一确定,前序里对应的两段也随之确定。 重建二叉树的原理见 树的基础与遍历 的「二叉树的三种遍历」一节。
实现上还有个更省事的观察:前序里节点出现的顺序,恰好就是深度优先地 「先建根、再建整棵左子树、最后建整棵右子树」的顺序。 所以只要保证处理顺序是这个顺序,就可以用一个只增不减的游标顺次取根, 完全不必去算「左子树在前序里占了哪一段」。 这里用显式栈维持这个顺序:弹出一个待建区间就消费一个前序元素, 压栈时先压右段后压左段,弹出时左段先到手。
示例 1:preorder = [3,9,20,15,7],inorder = [9,3,15,20,7]。
取 3 当根,它在中序的下标是 1
-> 左段是 inorder[0..0],右段是 inorder[2..4]
建左段:取 9 当根,中序区间只剩它自己,两侧都是空区间
建右段:取 20 当根,它在中序的下标是 3 -> 左段 [15]、右段 [7]
依次取 15、7 建成两个叶子
得到 [3,9,20,null,null,15,7],与样例一致。
数据规模与复杂度¶
1 <= preorder.length <= 3000,两数组等长,值域 [-3000, 3000] 且无重复元素。 中序下标先预处理成「值 -> 下标」的哈希表,建每个节点时定位根是 O(1), 总时间 O(n);若每次都用 inorder.index(val) 现找,单次 O(n), 链状树下总量退化成 3000 * 3000 = 9 * 10^6 次比较, 这个规模还扛得住,但一旦上界再抬一档就没救,哈希表是这里唯一值得写的优化。 空间 O(n):哈希表 n 项,栈深不超过树高。
坑在哪¶
- 必须用显式栈。n 到 3000,退化成一条链时递归深度就是 3000, 超过 CPython 默认的 1000 层上限,递归版会 RecursionError; 递归改迭代见 DFS 深度优先搜索 的 60.4 节。
- 压栈顺序是先右后左。栈是后进先出,写反了就会先建右子树, 而游标此刻取到的却是左子树的根,整棵树的形状全错。
- 空区间也要压栈、弹出后再跳过,且跳过时不能动游标。 空子树在前序里不占任何元素,误消费一个就等于把兄弟子树的根偷走了。
- 哈希表记的是中序下标。切分左右两段靠的是根在中序里的位置, 记成前序下标整个分解就失去意义。
- 值互不重复是题面给的前提,哈希表才能做到「值 -> 唯一下标」。 若允许重复,前序加中序不再唯一确定一棵树,这题的解法整个不成立。
参考实现¶
[:octicons-arrow-left-16: LC104](LC104.md) [LC108 :octicons-arrow-right-16:](LC108.md)