问题

根据一棵树的前序遍历与中序遍历构造二叉树

注意:
你可以假设树中没有重复的元素

例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:
3
/ \
9 20
/ \
15 7

解法一:递归

中序遍历中定位到根节点,那么我们就可以分别知道左子树右子树中的节点数目。由于同一颗子树的前序遍历中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。
这样以来,我们就知道了左子树前序遍历中序遍历结果,以及右子树前序遍历中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置

细节

在中序遍历中对根节点进行定位时,一种简单的方法是直接扫描整个中序遍历的结果并找出根节点,但这样做的时间复杂度较高。我们可以考虑使用哈希表来帮助我们快速地定位根节点。对于哈希映射中的每个键值对,键表示一个元素(节点的值),值表示其在中序遍历中的出现位置。在构造二叉树的过程之前,我们可以对中序遍历的列表进行一遍扫描,就可以构造出这个哈希映射。在此后构造二叉树的过程中,我们就只需要 O(1) 的时间对根节点进行定位了

  1. class Solution{
  2. int pre_idx;
  3. int[] preorder;
  4. int[] inorder;
  5. Map<Integer, Integer> idx_map = new HashMap<Integer, Integer>();
  6. public TreeNode helper(int in_left, int in_right){
  7. if(in_left > in_right){
  8. return null;
  9. }
  10. int root_val = preorder[pre_idx];
  11. TreeNode root = new TreeNode(root_val);
  12. int index = idx_map.get(root_val);
  13. pre_idx++;
  14. root.left = helper(in_left, index - 1);
  15. root.right = helper(index + 1, in_right);
  16. return root;
  17. }
  18. public TreeNode buildTree(int[] preorder, int[] inorder){
  19. this.preorder = preorder;
  20. this.inorder = inorder;
  21. pre_idx = 0;
  22. int idx = 0;
  23. for(Integer val : inorder){
  24. idx_map.put(val, idx++);
  25. }
  26. return helper(0, inorder.length - 1);
  27. }
  28. }
  1. //官解
  2. class Solution {
  3. private Map<Integer, Integer> indexMap;
  4. public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
  5. if (preorder_left > preorder_right) {
  6. return null;
  7. }
  8. // 前序遍历中的第一个节点就是根节点
  9. int preorder_root = preorder_left;
  10. // 在中序遍历中定位根节点
  11. int inorder_root = indexMap.get(preorder[preorder_root]);
  12. // 先把根节点建立出来
  13. TreeNode root = new TreeNode(preorder[preorder_root]);
  14. // 得到左子树中的节点数目
  15. int size_left_subtree = inorder_root - inorder_left;
  16. // 递归地构造左子树,并连接到根节点
  17. // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
  18. root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
  19. // 递归地构造右子树,并连接到根节点
  20. // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
  21. root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
  22. return root;
  23. }
  24. public TreeNode buildTree(int[] preorder, int[] inorder) {
  25. int n = preorder.length;
  26. // 构造哈希映射,帮助我们快速定位根节点
  27. indexMap = new HashMap<Integer, Integer>();
  28. for (int i = 0; i < n; i++) {
  29. indexMap.put(inorder[i], i);
  30. }
  31. return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
  32. }
  33. }
  34. 作者:LeetCode-Solution
  35. 链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/cong-qian-xu-yu-zhong-xu-bian-li-xu-lie-gou-zao-9/
  36. 来源:力扣(LeetCode
  37. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 时间复杂度:leetcode-105:从前序与中序遍历序列构造二叉树 - 图1,其中 n 是树中的节点个数
  • 空间复杂度:leetcode-105:从前序与中序遍历序列构造二叉树 - 图2,除去返回的答案需要的 leetcode-105:从前序与中序遍历序列构造二叉树 - 图3 空间之外,我们还需要使用 leetcode-105:从前序与中序遍历序列构造二叉树 - 图4 的空间存储哈希映射,以及 leetcode-105:从前序与中序遍历序列构造二叉树 - 图5(其中 h 是树的高度)的空间表示递归时栈空间。这里 h < n,所以总空间复杂度为 leetcode-105:从前序与中序遍历序列构造二叉树 - 图6

解法二:迭代

官解