力扣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]
解题思路:
先序遍历是“根左右”所以先序遍历数组中的第一个元素肯定是整棵树的根节点,中序遍历是“左根右”所以根节点将左子树的节点元素与右子树的节点元素分隔开来。我们首先确定整棵树的根节点head然后在中序遍历中找到根节点的位置find然后在先序遍历数组中根据find划出左子树节点元素的范围以及右子树节点元素的范围然后再递归地构建树。
代码:
/**
* 根据一棵树的先序遍历和中序遍历构建一棵树
* 首先我们应该根据先序遍历确定这棵树的根节点然后在中序遍历中找到这棵树的根节点
* 然后将中序遍历分为左右两部分,再根据这两部分所占的数量在先序遍历中确定左子树和右子树的部分
* 依次类推做递归最后确定这棵树
*/
public class BasePreAndInBuildTree {
//1.先定义一个二叉树的节点类
public static class TreeNode{
public int value;
public TreeNode left;
public TreeNode right;
public TreeNode(int value) {
this.value = value;
}
}
//2.定义根据先序遍历以及中序遍历构建二叉树的方法
public static TreeNode buildTree1(int[] pre,int[] in){
//2.1如果先序遍历为空或者中序遍历为空或者先序遍历数组的长度与中序遍历数组的长度不相等返回null
if(pre == null || in == null || pre.length != in.length){
return null;
}
return basePreAndInBuildTree(pre,0,pre.length-1,in,0,in.length-1);
}
/*
3.有一棵树先序遍历是pre[l1...r1]中序遍历是in[l2...r2]
根据这棵树的先序遍历以及中序遍历构建一棵树
*/
public static TreeNode basePreAndInBuildTree(int[] pre,int l1,int r1,int[] in,int l2,int r2){
if(l1 > r1){
return null;
}
//3.2在先序遍历的数组pre中取出根节点
TreeNode head = new TreeNode(pre[l1]);
//3.3如果先序遍历数组的第一个元素与中序遍历数组的第一个元素相等说明此树没有左子树
if(l1 == r1){
return head;
}
//3.4在中序遍历数组中寻找根节点的位置
int find = l2;
while (in[find] != pre[l1]){
find++;
}
//3.5递归地构建左子树,递归地构建右子树
/*
根据上一步我们找到了在中序遍历中根节点的位置find所以左子树节点的个数为find-l2所以在先序遍历数组中左子树节点的元素处于l1+1 到 l1+find-l2
在中序遍历数组in中左子树节点的元素处于l2 到 find-1
*/
head.left = basePreAndInBuildTree(pre,l1+1,l1+find-l2,in,l2,find-1);
/*
根据3.4我们找到了在中序遍历中根节点的位置find 且根据上一步知道左子树节点元素的右边界点为 l1+find-l2所以在先序遍历数组中 右子树节点的元素处于
l1+find-l2+1 到 r1 在中序遍历数组in中右子树节点元素的位置处于 find+1 到 r2
*/
head.right = basePreAndInBuildTree(pre,l1+find-l2+1,r1,in,find+1,r2);
//3.6最后返回创建好树的根节点
return head;
}
}
优化:
因为上面地方法每次在中序遍历数组中寻找根节点地位置的时候每次都需要遍历 效率不高,我们可以考虑将中序遍历数组放在一个map中将数组值作为key数组元素的下标作为value
代码:
/**
* 优化:上面每一次都需要遍历中序遍历数组找到根节点效率不高
* 我们可以将中序遍历数组存到一张表(map)中,值为key数组位置value
*/
//2.定义根据先序遍历以及中序遍历构建二叉树的方法
public static TreeNode buildTree2(int[] pre, int[] in) {
//2.1如果先序遍历为空或者中序遍历为空或者先序遍历数组的长度与中序遍历数组的长度不相等返回null
if (pre == null || in == null || pre.length != in.length) {
return null;
}
//2.2定义一个hashmap将中序遍历数组中元素放进map中 值作为key 数组下标作为value
HashMap<Integer, Integer> valueIndexMap = new HashMap<>();
for (int i = 0; i < in.length; i++) {
valueIndexMap.put(in[i], i);
}
return basePreAndInBuildTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
}
/*
3.有一棵树先序遍历是pre[l1...r1]中序遍历是in[l2...r2]
根据这棵树的先序遍历以及中序遍历构建一棵树
*/
public static TreeNode basePreAndInBuildTree(int[] pre, int l1, int r1, int[] in, int l2, int r2, HashMap<Integer, Integer> valueIndexMap) {
if (l1 > r1) {
return null;
}
//3.2在先序遍历的数组pre中取出根节点
TreeNode head = new TreeNode(pre[l1]);
//3.3如果先序遍历数组的第一个元素与中序遍历数组的第一个元素相等说明此树没有左子树
if (l1 == r1) {
return head;
}
//3.4在valueIndexMap中寻找根节点的位置
int find = valueIndexMap.get(pre[l1]);
//3.5递归地构建左子树,递归地构建右子树
/*
根据上一步我们找到了在中序遍历中根节点的位置find所以左子树节点的个数为find-l2所以在先序遍历数组中左子树节点的元素处于l1+1 到 l1+find-l2
在中序遍历数组in中左子树节点的元素处于l2 到 find-1
*/
head.left = basePreAndInBuildTree(pre, l1 + 1, l1 + find - l2, in, l2, find - 1);
/*
根据3.4我们找到了在中序遍历中根节点的位置find 且根据上一步知道左子树节点元素的右边界点为 l1+find-l2所以在先序遍历数组中 右子树节点的元素处于
l1+find-l2+1 到 r1 在中序遍历数组in中右子树节点元素的位置处于 find+1 到 r2
*/
head.right = basePreAndInBuildTree(pre, l1 + find - l2 + 1, r1, in, find + 1, r2);
//3.6最后返回创建好树的根节点
return head;
}
标签:遍历,中序,find,二叉树,l1,节点,先序
From: https://www.cnblogs.com/ygstudy/p/17019328.html