携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情
每日刷题 2022.08.20
- leetcode原题链接:leetcode.cn/problems/ma…
- 难度:中等
- 方法: 递归 / 单调栈
题目
- 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
- 创建一个根节点,其值为 nums 中的最大值。
- 递归地在最大值 左边 的 子数组前缀上 构建左子树。
- 递归地在最大值 右边 的 子数组后缀上 构建右子树。
- 返回 nums 构建的 最大二叉树 。
示例
- 示例1
输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:
- [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
- [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
- 空数组,无子节点。
- [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
- 空数组,无子节点。
- 只有一个元素,所以子节点是一个值为 1 的节点。
- [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
- 只有一个元素,所以子节点是一个值为 0 的节点。
- 空数组,无子节点。
- 示例2
输入: nums = [3,2,1]
输出: [3,null,2,null,1]
提示
1 <= nums.length <= 10000 <= nums[i] <= 1000nums中的所有整数 互不相同
解题思路
- 本题可以直接根据题意,使用递归函数的调用求解。因为将树中的每一个节点拆开来,其都是自己的小子树的根节点。
递归的具体解题步骤:
- 确认递归的边界条件:当区间只剩一个元素(叶子节点)或者没有元素(左/右节点不存在为
null)的时候,就需要停下来,因为此时不需要再划分了。 - 递归内部的逻辑:首先要取区间内的最大值作为当前的根节点
cur,再去依次递归遍历当前根节点的左子树left和右子树right。 - 递归的返回值:返回创建好的节点
- 递归需要传递的参数:当前的区间
arr(!!!注意:每次递归传递的区间都是不同的,已经处理过的区间就不需要再传递了) - 递归比较容易些,但是时间复杂度上是
o(n^2)
单调栈
- 用于:查找最近(大/小)的元素的问题。
- 联想到类似的题目:剑指offerII 038.每日温度
- 根据题目可知:每次都需要取区间内最大的数作为下一个根节点。而当我们在区间内找到一个根节点
root之后,还需要找左边区间中找到小于root的最大值作为左子树的根节点,同理:还需要找右边区间中找到小于root的最大值作为右子树的根节点。那么恰好符合了单调栈的特性,查找最近的元素。
单调栈的解题步骤:
- 遍历数组
nums中的每一个元素,创建一个新的栈stack。 - 当栈空的时候,直接将
nums中的元素ele压入到栈中。 - 当栈不空的时候,需要将
ele和栈顶的元素top作比较:1.ele小于top,直接将ele压入栈中,并将top.right = ele; 2.ele大于top,一直循环将栈中所有的小于ele的元素弹出,并依次将ele.left = top。 - 单调栈比较难想,但是时间复杂度只需要
o(n)
AC代码
- 递归
/**
* @param {number[]} nums
* @return {TreeNode}
*/
var constructMaximumBinaryTree = function(nums) {
// 当前的数组,
function dfs(arr){
if(arr.length === 0) return null;
if(arr.length === 1) {
return new TreeNode(arr[0], null, null);
}
// 长度不为1,则需要继续分解
let maxx = Math.max(...arr), idx = arr.indexOf(maxx),cur = new TreeNode(arr[idx]);
// 取左边
let l = arr.slice(0, idx);
cur.left = dfs(l);
let r = arr.slice(idx + 1);
// 取右边
cur.right = dfs(r);
return cur;
}
return dfs(nums);
};
- 单调栈
/**
* @param {number[]} nums
* @return {TreeNode}
*/
var constructMaximumBinaryTree = function(nums) {
const stack = [];
nums.push(10001) // 哨兵技巧
for (let i = 0; i < nums.length; i++) {
// 因为要出栈的时候构造树,所以栈中的元素都是节点
let curNode = new TreeNode(nums[i]);
while(stack.length && stack[stack.length - 1].val < curNode.val) {
// 出栈作为子节点
const childNode = stack.pop();
// 栈中无其他元素 或者 栈顶值是大于当前值的 举例:[3, 1, 2]
if (!stack.length || stack[stack.length - 1].val > curNode.val) {
curNode.left = childNode;
} else {
// 栈顶值小于当前值 举例:[2, 1, 3]
stack[stack.length - 1].right = childNode;
}
}
// // 因为要出栈的时候构造树,所以栈中的元素都是节点
stack.push(curNode);
}
return stack[0].left
};