每天题解:LeetCode 105. 从前序与中序遍历序列构造二叉树

Author Avatar
清水雅然君 05月 22,2020

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

题目链接

    3
   / \
  9  20

关于二叉树的遍历,其实有个比较好记的方法,假设有一个棵树,只有三个节点,左节点树,当前节点,右边节点。
假设需要打印中间节点(根节点),有三种输出方式
1.按照左、中、右,

9-3-20

由于中间节点在中间输出,称为前序遍历,同理
2.中、左、右
中序遍历

3-9-20

3.左、右、中
后续遍历

9-20-3

所以题目中的二叉树的遍历为

    3
   / \
  9  20
    /  \
   15   7

前序遍历

前序遍历(DLR),可记做根左右。前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。

3-9-20-15-7

中序遍历

中序遍历(LDR),中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。

9-3-15-20-7

后序遍历

后序遍历(LRD),首先遍历左子树,然后遍历右子树,最后访问根结点。

9-15-7-20-3

题目描述

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

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

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

解法

class Solution {
      int i=0;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int inoLen = inorder.length;
        return myBuildTree(preorder, inorder, 0, inoLen - 1);
    }

    private TreeNode myBuildTree(int[] preorder, int[] inorder, int start, int end) {
        //中止条件
        if (start > end) {
            return null;
        }
        int rootVal = preorder[i];
        //找到左边树的
        int left = start;
        for (int i = 0; i <= end; i++) {
            if (inorder[i] == rootVal) {
                left = i;
            }
        }
        //start -left-1 都是左边树 右边 left+1 到 end都是右边树
        i++;
        TreeNode root = new TreeNode(rootVal);
        root.left = myBuildTree(preorder, inorder, start, left-1);
        root.right = myBuildTree(preorder, inorder, left + 1, end);
        return root;
        }
}

分析思路

1.首先分析前序遍历和中序遍历
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
从前序遍历的规律可以知道,3 二叉树的根节点,从中序遍历的特点(先输出左,再输出根,最后输出右),可以知道根节点3的左边只有一个节点9,后面的15 ,20 ,7是其右边树
先构建一棵树

    3
   / \
  9  待定[15,20,7]

再来分析右边树{15,20,7]
1.按照前序遍历得到右边的树的根节点是20,在从中序遍历,知道 左边是15,右边是7

   20
   /  \
  15  7 

最后将两课树组装

    3
   / \
  9  20
    /  \
   15   7

2.拆分
从上面的分析可以知道,我们如何使用前序和中序结合,构建二叉树的步骤
一、从前序遍历得到根节点
二,前序遍历得到右边树,右边树

2.1前序遍历先取值 3 
中序遍历得到 左边 9, 右边 15,20,7
2.2前序遍历往后移一位,取值9
中序遍历得到 左边 null,右边 null
2.3 前序遍历往后移一位,取值20
中序遍历得到 左边 为15,右边 7
2.3 前序遍历往后移一位,取值15
中序遍历得到 左边 为null,右边 null
2.3 前序遍历往后移一位,取值7
中序遍历得到 左边 为null,右边 null

按照这个思路,我们可以写出规律
设置一个全局遍历i,维护前序遍历的左移动的位置
// 从前序遍历得到中间节点或者

 int rootVal = preorder[i];

// 遍历中序得到 左边树到根节点的位置

 for (int i = 0; i <= end; i++) {
           if (inorder[i] == rootVal) {
               left = i;
           }
       }

构建根节点的树

 TreeNode root = new TreeNode(rootVal);

变量后移一位

i++;

以上就是整个遍历的循环体的代码
接下来要构建递归的的整体代码
1.参数
前序遍历,中序遍历,开始的位置,结束的位置
2.结束条件,开始的位置大于结束位置

 if (start > end) {
     return null;
 }

3.循环体

 int rootVal = preorder[i];
// 找到左边树的结束位置
//从开始位置累加到结束位置,如[15,20,7]  这里的开始位置就是 2
        int left = start;
        for (int i = 0; i <= end; i++) {
            if (inorder[i] == rootVal) {
                left = i;
            }
        }
        i++;
        TreeNode root = new TreeNode(rootVal);

4.递归左,右树
// start -left-1 都是左边树,右边 left+1 到 end都是右边树

root.left = myBuildTree(preorder, inorder, start, left-1);
root.right = myBuildTree(preorder, inorder, left + 1, end);

5.返回结果

return root;