105. 从前序与中序遍历构造二叉树(Construct Binary Tree from Preorder and Inorder Traversal)

频次 ★★★★ · 难度 🟡 · 高频:字节/腾讯

题目

根据前序和中序遍历结果重建二叉树(树中无重复值)。

示例

前序: [3,9,20,15,7]
中序: [9,3,15,20,7]
输出: 二叉树 3 → 9 / 20 → 15 / 7

思路

分治递归

  • 前序第一个元素是根节点
  • 在中序中找到根位置,左侧是左子树、右侧是右子树
  • 前序中紧跟根之后的「左子树长度」个元素是左子树的前序序列,再之后是右子树前序
  • 递归处理

优化:用 Map<值, 中序下标> 把根定位加速到 O(1)。

代码

private Map<Integer, Integer> inMap;  // 值 → 中序索引
 
public TreeNode buildTree(int[] preorder, int[] inorder) {
    inMap = new HashMap<>();
    for (int i = 0; i < inorder.length; i++) inMap.put(inorder[i], i);
    return build(preorder, 0, preorder.length - 1, 0);
}
 
// build 参数: 前序数组, 前序左边界, 前序右边界, 中序左边界
private TreeNode build(int[] pre, int preL, int preR, int inL) {
    if (preL > preR) return null;
    TreeNode root = new TreeNode(pre[preL]);
    int inIdx = inMap.get(root.val);       // 根在中序中的位置
    int leftSize = inIdx - inL;            // 左子树节点数
    root.left = build(pre, preL + 1, preL + leftSize, inL);
    root.right = build(pre, preL + leftSize + 1, preR, inIdx + 1);
    return root;
}

复杂度

  • 时间:O(n) —— HashMap 加速中序查找
  • 空间:O(n) —— HashMap + 递归栈

边界条件

  • 长度为 0:返回 null
  • 单节点:返回该节点
  • 左/右单支树:leftSize = 0 或 rightSize = 0

变式

易错点

  • 前序中左子树范围的边界计算preL + 1(左子树前序起点)到 preL + leftSize(左子树前序终点),preL + leftSize + 1(右子树前序起点)。多一个少一个都错
  • 中序的边界用 inLinIdx,不要引入 inR,因为可以从 leftSize 反推
  • 没有重复值这个条件很重要——有重复值时无法确定根在中序中的位置,需要其他约束

面试追问

  • 为什么前序 + 中序能唯一确定一棵二叉树? 前序提供根,中序提供左右划分——两序列各自贡献一部分信息。如果只给前序 + 后序,当树不是满二叉树时,结果不唯一
  • 不用 HashMap 的优化思路? 每次在中序中线性查找根,最坏 O(n²),但可以先答”Hash 优化到 O(n)“再简述”如果面试官说不准用额外空间,可以用双指针推进中序”

关联题