题目:

105. 从前序与中序遍历序列构造二叉树

题解:

LeetCode 第 105 题视频讲解

1. 题解一:

抓住“前序遍历的第 1 个元素一定是二叉树的根结点”,不难写出代码。关键还是拿 LeetCode 上面的例子画一个图,思路就很清晰了。

前序遍历数组的第 11 个数(索引为 00)的数一定是二叉树的根结点,于是可以在中序遍历中找这个根结点的索引,然后把“前序遍历数组”和“中序遍历数组”分为两个部分,就分别对应二叉树的左子树和右子树,分别递归完成就可以了。

下面是一个具体的例子,演示了如何计算数组子区间的边界:

2. 题解二:

可以将中序遍历的值和索引存在一个哈希表中,这样就可以一下子找到根结点在中序遍历数组中的索引。

3. 题解三:

4. 题解四:

目标:如何从先序与中序遍历序列构造二叉树
分析:这一类题需要我们把中序链表放入 map 中,根据先序的顺序来递归出每个节点的左子树和右子树
错误:这题需要先遍历左子树再遍历右子树,不要乱顺序
关键:以中序为基础,以先序链表从前往后的顺序构建出每个节点的左子树和右子树

代码:

1. 代码一:

/** * code105 */
public class code105 {

    public static TreeNode buildTree(int[] preorder, int[] inorder) {
        int preLen = preorder.length;
        int inLen = inorder.length;
        return build(preorder, 0, preLen - 1, inorder, 0, inLen - 1);
    }

    /** * 使用数组 preorder 在索引区间 [preLeft, preRight] 中的所有元素 * 和数组 inorder 在索引区间 [inLeft, inRight] 中的所有元素构造二叉树 * * @param preorder 二叉树前序遍历结果 * @param preLeft 二叉树前序遍历结果的左边界 * @param preRight 二叉树前序遍历结果的右边界 * @param inorder 二叉树后序遍历结果 * @param inLeft 二叉树后序遍历结果的左边界 * @param inRight 二叉树后序遍历结果的右边界 * @return 二叉树的根结点 */

    public static TreeNode build(int preorder[], int preLeft, int preRight, int inorder[], int inLeft, int inRight) {
        // 因为是递归调用的方法,按照国际惯例,先写递归终止条件
        if (preLeft > preRight || inLeft > inRight) {
            return null;
        }
        // 先序遍历的起点元素很重要
        int pivot = preorder[preLeft];
        TreeNode root = new TreeNode(pivot);
        int pivotIndex = inLeft;
        // 严格上说还要做数组下标是否越界的判断 pivotIndex < inRight
        while (inorder[pivotIndex] != pivot) {
            pivotIndex++;
        }
        root.left = build(preorder, preLeft + 1, preLeft + pivotIndex - inLeft, inorder, inLeft, pivotIndex - 1);
        root.right = build(preorder, preLeft + pivotIndex - inLeft + 1, preRight, inorder, pivotIndex + 1, inRight);
        return root;
    }

    public static void main(String[] args) {
        int preorder[] = { 3, 9, 20, 15, 7 };
        int inorder[] = { 9, 3, 15, 20, 7 };
        TreeNode tree = buildTree(preorder, inorder);
        System.out.println("***************************************");
        TreeOperation.show(tree);
        System.out.println("***************************************");
    }

}

2. 代码二:


/** * code105 */
import java.util.*;

public class code105 {

    public static TreeNode buildTree(int[] preorder, int[] inorder) {
        int preLen = preorder.length;
        int inLen = inorder.length;
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < inLen; i++) {
            map.put(inorder[i], i);
        }
        return build(preorder, 0, preLen - 1, 0, inLen - 1, map);
    }

    /** * 使用数组 preorder 在索引区间 [preLeft, preRight] 中的所有元素 和数组 inorder 在索引区间 [inLeft, * inRight] 中的所有元素构造二叉树 * * @param preorder 二叉树前序遍历结果 * @param preLeft 二叉树前序遍历结果的左边界 * @param preRight 二叉树前序遍历结果的右边界 * @param inorder 二叉树后序遍历结果 * @param inLeft 二叉树后序遍历结果的左边界 * @param inRight 二叉树后序遍历结果的右边界 * @return 二叉树的根结点 */

    public static TreeNode build(int preorder[], int preLeft, int preRight, int inLeft, int inRight,
            Map<Integer, Integer> map) {
        // 因为是递归调用的方法,按照国际惯例,先写递归终止条件
        if (preLeft > preRight || inLeft > inRight) {
            return null;
        }
        // 先序遍历的起点元素很重要
        int pivot = preorder[preLeft];
        TreeNode root = new TreeNode(pivot);
        int pivotIndex = map.get(pivot);
        root.left = build(preorder, preLeft + 1, preLeft + pivotIndex - inLeft, inLeft, pivotIndex - 1, map);
        root.right = build(preorder, preLeft + pivotIndex - inLeft + 1, preRight, pivotIndex + 1, inRight, map);
        return root;
    }

    public static void main(String[] args) {
        int preorder[] = { 3, 9, 20, 15, 7 };
        int inorder[] = { 9, 3, 15, 20, 7 };
        TreeNode tree = buildTree(preorder, inorder);
        System.out.println("***************************************");
        TreeOperation.show(tree);
        System.out.println("***************************************");
    }

}

3. 代码三:


/** * code105 */
import java.util.*;

public class code105 {

    // 解法三:
    public static int index;

    public static TreeNode buildTree(int[] preorder, int[] inorder) {
        index = 0;
        return build(preorder, inorder, 0, inorder.length - 1);
    }

    public static TreeNode build(int preorder[], int inorder[], int inStart, int inEnd) {
        if (inStart > inEnd) {
            return null;
        }
        int val = preorder[index++];
        for (int i = inStart; i <= inEnd; i++) {
            if (inorder[i] == val) {
                TreeNode root = new TreeNode(val);
                root.left = build(preorder, inorder, inStart, i - 1);
                root.right = build(preorder, inorder, i + 1, inEnd);
                return root;
            }
        }
        return null;
    }

    public static void main(String[] args) {
        int preorder[] = { 3, 9, 20, 15, 7 };
        int inorder[] = { 9, 3, 15, 20, 7 };
        TreeNode tree = buildTree(preorder, inorder);
        System.out.println("***************************************");
        TreeOperation.show(tree);
        System.out.println("***************************************");
    }

}

4. 代码四:


/** * code105 */
import java.util.*;

public class code105 {

    // 解法四:
    public static int preIndex;// 前序的索引

    public static TreeNode buildTree(int[] preorder, int[] inorder) {
        preIndex = 0;// 因为二叉树的第一个节点对应前序的第一个,从第一个开始
        Map<Integer, Integer> map = new HashMap<>();// 存放中序的映射
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }
        return build(preorder, 0, inorder.length - 1, map);
    }

    /** * * @param preorder 前序链表 * @param inLeft 中序的左边界 * @param inRight 中序的右边界 * @return 二叉树节点 */

    public static TreeNode build(int preorder[], int inLeft, int inRight, Map<Integer, Integer> map) {
        if (inLeft > inRight) {
            return null;
        }
        int val = preorder[preIndex++];// 从第一个开始初始化为TreeNode
        TreeNode root = new TreeNode(val);
        int index = map.get(val);// 得到在中序的索引
        root.left = build(preorder, inLeft, index - 1, map);// 再遍历左子树
        root.right = build(preorder, index + 1, inRight, map);// 先遍历右子树
        return root;
    }

    public static void main(String[] args) {
        int preorder[] = { 3, 9, 20, 15, 7 };
        int inorder[] = { 9, 3, 15, 20, 7 };
        TreeNode tree = buildTree(preorder, inorder);
        System.out.println("***************************************");
        TreeOperation.show(tree);
        System.out.println("***************************************");
    }

}

参考:

  1. 分治法(Python 代码、Java 代码)
  2. 从前序和中序遍历序列构造二叉树
  3. 思路清晰,代码简洁,和106题思路一致(Python)
  4. 详细通俗的思路分析,多解法
  5. 递归解法,思路清晰
  6. C++ 图解过程
  7. LeetCode105. 从前序与中序遍历序列构造二叉树
  8. 前序中序遍历构造二叉树(模拟法,代码清晰、精简)
  9. 使用 map
  10. 使用Map
  11. Java map 详解 - 用法、遍历、排序、常用API等
  12. 【深入Java基础】HashMap的基本用法