leetcode刷题日记-【105. 从前序与中序遍历序列构造二叉树】

64 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情

题目描述

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

 

示例 1:

image.png 输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7] 示例 2:

输入: preorder = [-1], inorder = [-1] 输出: [-1]  

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder 和 inorder 均 无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

解题思路

这道题在面试中经常遇见,算是二叉树的基础问题。 给定一个二叉树的前序遍历数组preorder,一个二叉树的中序遍历数组inorder,求这棵二叉树;

二叉树的前序遍历:

先获取到根节点,然后再递归地遍历左叶子树,然后再递归地遍历右叶子树;

中序遍历:

先递归地遍历左叶子树,再获取根节点,然后再递归地遍历右叶子树;

所以,其实前中后序描写的是根节点的位置,然后左节点一定在右节点之前

这样三种遍历方式就可以简单描述成 前-左-右;左-前-右;左-右-前

所以可以得出,前序中的第一个元素一定是整个二叉树的根节点;随后是它的左叶子树的节点遍历元素然后是右叶子节点的节点遍历元素;

中序中根节点之前一定是左叶子树的节点遍历元素,根节点之后一定是右叶子节点的节点遍历元素

依次循环,则可构建出一棵完整的二叉树序列;

因为这里涉及到需要查找到具体元素的位置,所以需要先将两个序列中的元素位置进行存储(给定二叉树中无重复元素);

首先在中序数组中找到根节点的位置,根节点之前的是左叶子节点的中序遍历结果;右叶子节点的是右叶子节点的中序遍历结果;相同的节点不管是什么样的遍历方式,它的长度一定是一致的;

所以能够根据中序数组中获取到的左右叶子节点的中序遍历长度,去前序列表中查找到对应的左右叶子节点的前序遍历结果;

每次循环都将同个节点的前序和中序放在一起循环,这样始终都能找到它下面一层的左右遍历树。每一次循环都能从前序数组中获取到当前节点的根节点,循环至左子树的位置大于右子树的位置时,则结束循环;

构建出的二叉树就是完整二叉树。

代码实现

public static TreeNode buildTree(int[] preorder, int[] inorder) {
    // 构建中序map
    HashMap<Integer, Integer> inMap = new HashMap<>();
    for (int i=0; i<inorder.length; i++) {
        inMap.put(inorder[i],i);
    }
    // 递归
    return findTree(preorder, inMap, 0, preorder.length - 1, 0, inorder.length - 1);
}

/**
 * 构建二叉树
 *
 * @param preorder 前序
 * @param inMap 中序元素map,方便查找根节点位置
 * @param preLeft 前序的左边界
 * @param preRight 前序的右边界
 * @param inLeft 中序的左边界
 * @param inRight 中序的右边界
 */
private static TreeNode findTree(int[] preorder, HashMap<Integer, Integer> inMap, int preLeft, int preRight, int inLeft, int inRight) {
    if (preLeft > preRight || inLeft > inRight) {
        return null;
    }
    int rootValue = preorder[preLeft];
    TreeNode treeNode = new TreeNode();
    treeNode.val = rootValue;
    Integer rootIndex = inMap.get(rootValue);
    // 获取左叶子树中序遍历长度和右叶子树中序遍历长度
    int leftLength = rootIndex - inLeft;
    // 前序中左叶子树的遍历位置
    treeNode.left = findTree(preorder,inMap,preLeft+1,preLeft+leftLength,inLeft,rootIndex-1);
    treeNode.right = findTree(preorder,inMap,preLeft+leftLength+1,preRight,rootIndex+1,inRight);
    return treeNode;
}