剑指 Offer 07. 重建二叉树
题目:找重建二叉树
-
LeetCode地址:剑指 Offer 07. 重建二叉树
-
描述:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
-
示例:
给出: 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 返回如下二叉树: 3 / \ 9 20 / \ 15 7
题解:
方法一:递归
-
思路:
前序遍历性质: 节点按照
[ 根节点 | 左子树 | 右子树 ]
排序。
中序遍历性质: 节点按照[ 左子树 | 根节点 | 右子树 ]
排序。- 拿给出的示例来说,从前序遍历我们以可知道 3 必定是根节点;
- 又根据 3 在中序遍历数组中的位置可以知 [ 9 ] 必定在其左子树;[ 15, 20, 7 ] 必定在其右子树;
- 再看前序遍历数组,可知 [ 9 ] 是 3 节点左子树前序遍历的结果,同时 [ 20, 15, 7 ]是 3 节点右子树前序遍历的结果
- 有了上面的分析,我们就可对3节点的左、右子树分别按同样的步骤推理,直到推出整个二叉树。
-
代码:
public class Solution { int[] preorder; //存放中序遍历元素值和对应索引 key=元素值 value=中序遍历索引 HashMap<Integer, Integer> inIndexMap = new HashMap<>(); public TreeNode buildTree(int[] preorder, int[] inorder) { this.preorder = preorder; int length = preorder.length; //中序遍历结果存入inIndexMap for(int i = 0; i < length; i++) inIndexMap.put(inorder[i], i); return buildTree(0, 0, length - 1); } /** * * @param nodePreIndex 当前节点在前序遍历数组中的索引 * @param left 中序遍历左边界 * @param right 中序遍历右边界 * @return 生成的节点 */ TreeNode buildTree(int nodePreIndex, int left, int right) { if(left > right) return null; int nodeVal = preorder[nodePreIndex]; //根据节点值创建当前节点 TreeNode node = new TreeNode(nodeVal); //获取当前节点中序遍历的索引 int nodeInIndex = inIndexMap.get(nodeVal); //递归创建当前节点的左、右子树 node.left = buildTree(nodePreIndex + 1, left, nodeInIndex - 1); //nodeIndex-left表示左子树节点个数 //nodePreIndex+nodeIndex-left+1 理解为:当前节点前序索引+左子树节点个数+1 也就是右子节点的索引 node.right = buildTree(nodePreIndex + nodeInIndex - left + 1, nodeInIndex + 1, right); return node; } public static void main(String[] args) { int[] preorder = {3, 9, 20, 15, 7}; int[] inorder = {9, 3, 15, 20, 7}; TreeNode treeNode = new Solution().buildTree(preorder, inorder); System.out.println("treeNode = " + treeNode); } }
-
复杂度分析:
- 时间复杂度:O(n) 。其中 N 为树的节点数量。初始化 HashMap 需遍历 inorder ,占用 O(N) 。递归共建立 N个节点,每层递归中的节点建立、搜索操作占用 O(1) ,因此使用 O(N) 时间。
- 空间复杂度:O(n)。HashMap 使用 O(N) 额外空间。最差情况下,树退化为链表,递归深度达到 N ,占用 O(N) 额外空间;最好情况下,树为满二叉树,递归深度为 logN ,占用 O(logN) 额外空间。
-
测试: