携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第8天,点击查看活动详情
前言
小白算法比较菜,希望能激励我每日更新,从leetcode第一题开始,2022年目标300题,记录从0到1的全过程!!
剑指 Offer 07. 重建二叉树
剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例1
Input: preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
示例2
Input: preorder = [-1]
inorder = [-1]
Output: [-1]
2.解析
前两天写了斐波那契数、爬楼梯、楼梯最小花费和机器人路径数,都分享了两个做法,递归和记忆数组。今天继续复习这一知识点。想看前三题的点下面链接。
# [杨小白]_java_leetcode 509.斐波那契数
# [杨小白]_java_leetcode 746. 使用最小花费爬楼梯
# [杨小白]_java_leetcode 62. 不同路径
2.1解法(递归法)
前序遍历的root节点,在中序遍历的中间,中序遍历的root节点之前,是left子树,root节点之后是right子树,运用这一特征就很好的解决了root节点的问题,对于root节点来说,root.val = preorder[0],root.left 就是对中序遍历root节点之前的进行重建,root.right 就是对中序遍历root节点之后的进行重建。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length==0) return null;
TreeNode temp = new TreeNode(preorder[0]);
for(int i = 0; i < preorder.length; i++) {
if(preorder[0]==inorder[i]) {
temp.left = buildTree(Arrays.copyOfRange(preorder,1,i+1),Arrays.copyOfRange(inorder,0,i));
temp.right = buildTree(Arrays.copyOfRange(preorder,i+1,preorder.length),Arrays.copyOfRange(inorder,i+1,inorder.length));
break;
}
}
return temp;
}
}
提交排名
代码简洁明了,排名几乎垫底。
2.2解法(记忆数组)
解法一的问题在于,需要cope数组,浪费了大量时间,那么我们能不能直接把原数组的l和r作为双指针传入递归函数呢?并且用一个map储存了前序遍历中的值,在中序遍历中所对应的位置。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private Map<Integer, Integer> inOrder_Index_Map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length==0) return null;
inOrder_Index_Map = new HashMap<>();
for(int i = 0; i < inorder.length; i++) {
inOrder_Index_Map.put(inorder[i],i);
}
return build(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
}
public TreeNode build(int[] preorder, int start_preorder,int end_preorder, int[] inorder,int start_inorder, int end_inorder){
if(start_preorder>end_preorder||start_inorder>end_inorder){
return null;
}
TreeNode temp = new TreeNode(preorder[start_preorder]);
int pindex = inOrder_Index_Map.get(preorder[start_preorder]);
int sp = start_preorder + 1;
int ep = start_preorder + pindex - start_inorder;
int si = start_inorder;
int ei = pindex - 1;
temp.left = build(preorder,sp,ep,inorder,si,ei);
sp = start_preorder + 1 + pindex -start_inorder;
ep = end_preorder;
si = pindex + 1;
ei = end_inorder;
temp.right = build(preorder,sp,ep,inorder,si,ei);
return temp;
}
}
提交排名
时间复杂度大大减小,内存消耗也减少了一半。
其中难以理解的是sp、ep、si、ei的求解,sp是start_preorder前序遍历起点的缩写,ep是end_preorder前序遍历终点的缩写,si是start_inorder中序遍历起点的缩写,ei是end_inorder中序遍历终点的缩写。
3.结束
利用原有的数据,只用较少的指针性的值去记录不同的情况,避免需要反复创建新的对象,以此来减少递归的复杂度。
递归因为有其优良的简便性,随之而来的就是时间,空间的大量占用,所以每一个简单递归算法的背后,都有大量可以优化的点,大家可以仔细钻研总结。