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

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

示例 1:

img

输入: 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
  • preorderinorder无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

# 思路分析:

这个题目的意思是给了你 一颗二叉树它的 前序,中序遍历结果了

比如说这颗二叉树的前序遍历结果是:1,2,4,3,6,7

这颗二叉树的中序遍历结果是:4,2,1,6,3,7

根据上面两个二叉树的遍历结果 二叉树创建出来应该是如下样子:

image-20240130102702438

我们分析一下 还原二叉树的 过程:

image-20240130103359529

我们通过 前序遍历 就看得知 root 节点肯定是 1 ,但是我们继续从 前序遍历中找的 话 我们就很难分辨出哪个是 root 的左子树 和 右子树了,虽然我们看上面完整的二叉树我们得知了 root 的左子树和 右子树是 2,3。但是 还可能有一种情况使它 的 左子树 和 右子树为 2,6,所以我们不能 根据前序遍历确定它这个树 长什么样子

这时我们再结合中序遍历的结果,中序遍历的特点是:先打印完 左子树 然后就是 中值,最后是 右子树,我们再看 中序遍历的结果:4,2,1,6,3,7。从 前序遍历中我们已知了 root 节点肯定是 1 那么 从中序遍历中看的话,1 是 root 而 1 左边就是 左子树的节点,而 1 的右边就是 右子树的节点了

所以根据 中序遍历 我们确定了 1 的左子树分别是: 2,4。1 的右子树 分别是:3,6,7

通过上面的了解得出如下:

 根 1                      根 1            
  pre                       inf
左 2,4                    左 4,2
右 3,6,7                  右 6,3,7

通过上面列出的我们发现这好像是一个子问题

如果我们不看上面的东西了,我们就看这次 2,4 这个结果 你怎么分辨出 哪个是根节点,哪个是左 / 右子树?

我们先从 根节点 查看 我们从 前序遍历中 找到 第一个元素就是根节点 也就是 2,随后我们看 中序遍历 的 4 在 2 的左边说明了 4 是 左子树 得到答案如下:

  2
 /
4

那我们再看 结果为:6,3,7 的呢?

我们先看前序遍历中 3 是 第一个元素 那么它就是根节点,确定了根节点,我们再看中序遍历 6 在左边 那它就是左子树,7 在 3 的右边 那 7 就是 右子树 ,得到答案如下:

  3
 / \
6   7

就这样的向下 找 最后 节点为 null 了,我们就可以停止子问题的递归了。

所以这道题 比较直接的 方法就是 使用递归的方法,根据两组遍历结果 去 确定根 确定左边有哪些,确定右边有哪些。知道没办法再次切分为止

# 代码实现:

public class BinaryDemo02 {
    public static void main(String[] args) {
        int preOrderArray[] = {1, 2, 4, 3, 6, 7};
        int infixOrderArray[] = {4, 2, 1, 6, 3, 7};
        BinaryTree b = new BinaryTree();
        HeroNode heroNode = new BinaryDemo02().buildTree(preOrderArray, infixOrderArray);
        b.setRoot(heroNode);
        LinkedList<HeroNode> s = new LinkedList<>();
        HeroNode root = heroNode;
        while(root != null || ! s.isEmpty()) {
            if(root != null) {
                s.push(root);
                System.out.println("前序遍历:" + root);
                root = root.getLeft();
            }else {
                HeroNode pop = s.pop();
                root = pop.getRight();
            }
        }
    }
    public HeroNode buildTree(int preOrder[], int inOrder[]) {
        if(preOrder.length == 0 || inOrder.length == 0) return null;
        // 通过前序遍历我们获取 root 节点 的值
        int rootValue = preOrder[0];
        // 将 root 节点 赋值给 节点类
        HeroNode root = new HeroNode(rootValue);
        for (int i = 0; i < inOrder.length; i++) {
            // 循环 如果 当前索引值等于 root 节点说明找到了 root 节点的索引位置了
            if (inOrder[i] == rootValue) {
                // 从 0 到 i - 1 就是左子树
                //copyOfRange 根据 区间 复制 一个数组 参数:inOrder 要复制的数组 区间:从 0 到 i-1 (含头不含尾 所以 i 就是 i - 1 的效果)
                int[] inLeft = Arrays.copyOfRange(inOrder, 0, i);// [4. 2]
                // 从 i + 1 到 inOrder.length - 1 就是右子树
                int[] inRight = Arrays.copyOfRange(inOrder, i + 1, inOrder.length);// [6, 3, 7]
                // 从 1 到 i - 1 就是左子树 因为需要 略过 0 索引的值 因为它就是 根节点 而我们要找的是 4,2 中谁是根 这时我们需要前序遍历的结果来判断
                int[] preLeft = Arrays.copyOfRange(preOrder, 1, i + 1);// [2, 4]
                // 从 i + 1 到 preOrder.length - 1 就是右子树
                int[] preRight = Arrays.copyOfRange(preOrder, i + 1, preOrder.length);// [3, 6, 7]
                root.setLeft(buildTree(preLeft, inLeft));// 2
                root.setRight(buildTree(preRight, inRight));// 3
                break;
            }
        }
        return root;
    }
}

打印结果:

前序遍历:1
前序遍历:2
前序遍历:4
前序遍历:3
前序遍历:6
前序遍历:7