从前序与中序遍历序列构造二叉树
来源:LeetCode:105
题目描述:
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: 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保证 为二叉树的中序遍历序列
解题思路:
1.了解先序遍历,中序遍历的含义:
先序遍历: 依次遍历 根节点->左节点->右节点
中序遍历: 依次遍历 左节点->根节点->右节点
2.我们可以发现,一棵树的节点遍历:
(1)先序遍历中的第一个数永远是(根节点),右边是(左子树)(右子树),但不知道分布位置(也就是各自的数量)。
(2)中序遍历中根节点永远将两边划分为(左子树),(右子树)。
(3)由此,我们可以在先序遍历中找到根节点的val,再去中序遍历中找到该val对应根节点的索引(因为题目告诉是一个不重复的二叉树),我们用哈希表来映射节点对应的val值。
(4)找到在中序遍历中的根节点的位置,我们再进行划分,就可以知道左子树节点的数量,然后就可以在先序遍历中划分左子树和右子树的位置。
(5)最后找到先序和中序各自的左子树,右子树数组中位置,我们让两个左右树新的数组对应 再次递归进行第(1)步,不停往复。(两个新的左子树遍历数组 进行递归,两个新的左右子树遍历数组 进行递归)
(6)直至左边索引大于右边索引,递归结束。
代码实现:
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        Map<Integer,Integer> map = new HashMap<>();
        int len1 = preorder.length;
        int len2 = inorder.length;
        //映射节点与索引的关系
        for(int i=0;i<len1;i++){
            map.put(inorder[i],i);
        }
        return buildTree(preorder ,0,len1-1,inorder,0,len2-1,map);
    }
    TreeNode buildTree(int[] preorder,int pre_l,int pre_r ,int[] inorder,int inor_l ,int inor_r,Map<Integer,Integer> map){
        //如果先序遍历数组左边边界大于右边,返回null
       if(pre_l>pre_r){
           return null;
       }
         //如果中序遍历数组左边边界大于右边,返回null
       if(inor_l>inor_r){
           return null;
       }
        //先序遍历的第一个数==每棵树的根节点
        int root_val = preorder[pre_l];
        TreeNode root = new TreeNode(root_val);
        //在hash获取中序遍历中的根节点的位置,目的:计算左右子树的节点数量,来划分新的子树,进行递归
        int inor_index = map.get(root_val); 
        //左子树
        /*
        pre_l+1:先序遍历左子树的左边界
        pre_l+(inor_index - inor_l):先序遍历左子树的右边界
        inor_l:中序遍历左子树的左边界
        inor_index-1:中序遍历左子树的右边界
        */
        root.left = buildTree(preorder,pre_l+1,pre_l+(inor_index - inor_l),inorder,inor_l,inor_index-1,map);
        //右子树
        /*
        preorder,pre_l+(inor_index - inor_l)+1:先序遍历右子树的左边界
        pre_r:先序遍历右子树的右边界
        inor_index+1:中序遍历右子树的左边界
        inor_r:中序遍历右子树的右边界
        
        */
        root.right = buildTree(preorder,pre_l+(inor_index - inor_l)+1,pre_r,inorder,inor_index+1,inor_r,map);
        return root;
    }
} 
