从前序与中序遍历序列构造二叉树
来源: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;
}
}