[递归 / 单调栈] 654. 最大二叉树

294 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情

每日刷题 2022.08.20

题目

  • 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
    • 创建一个根节点,其值为 nums 中的最大值。
    • 递归地在最大值 左边 的 子数组前缀上 构建左子树。
    • 递归地在最大值 右边 的 子数组后缀上 构建右子树。
    • 返回 nums 构建的 最大二叉树 。

示例

  • 示例1

image.png

输入: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

image.png

输入: nums = [3,2,1]
输出: [3,null,2,null,1]

提示

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 1000
  • nums 中的所有整数 互不相同

解题思路

  • 本题可以直接根据题意,使用递归函数的调用求解。因为将树中的每一个节点拆开来,其都是自己的小子树的根节点。

递归的具体解题步骤:

  • 确认递归的边界条件:当区间只剩一个元素(叶子节点)或者没有元素(左/右节点不存在为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
};