【前端er每日算法】单调栈再2题

46 阅读2分钟

题目一 503. 下一个更大元素 II

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

思路

  1. 循环数组,则通过遍历两遍数组模拟循环
  2. 从栈顶到栈尾单调递增栈,栈里元素是数组的值
    • 遇到比栈顶元素大的数时开始操作: 弹出栈顶元素,并且记录result的值,直到遇到大于等于它的元素
    • 比栈顶元素小或者等于,则直接入栈
var nextGreaterElements = function(nums) {
    const len = nums.length;
    const result = new Array(len).fill(-1);
    const stack = [0];
    for (let i = 1; i < 2 * len; i++) {
        const val = nums[i % len];
        while (stack.length && nums[stack[stack.length - 1]] < val) {
            const top = stack.pop();
            result[top % len] = val;
        }
        stack.push(i % len);
    }
    return result;
};

题目二 42. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

思路

方法1 双指针法 按照列来计算每一列可以接水量,每一列的宽度等于1,高度等于min(左侧最大元素,右侧最大元素) - 改列的高度, 所以每一列容量是 height * 1,这样每一列加起来就是总和。

可以提前求出来每一个位置的左侧高度最大值,和右侧元素最大值,这样减少时间复杂度。

// 双指针法
var trap = function(height) {
    const len = height.length;
    const lMaxHeight = new Array(len).fill(0);
    lMaxHeight[0] = height[0];
    const rMaxHeight = new Array(len).fill(0);
    rMaxHeight[len - 1] = height[len - 1];

    // lMaxHeight[i] = max(height[i], lmaxHeight[i-1])
    for (let i = 1; i < len - 1; i++) {
        lMaxHeight[i] = Math.max(height[i], lMaxHeight[i-1]);
    }
    // rMaxHeight[i] = max(height[i], lmaxHeight[i+1])
    for (let i = len - 2; i > 0; i--) {
        rMaxHeight[i] = Math.max(height[i], rMaxHeight[i+1]);
    }

    let result = 0;
    for (let i = 1; i < len - 1; i++) {
        const h = Math.min(lMaxHeight[i], rMaxHeight[i]) - height[i];
        if (h > 0) {
            result += h;
        }
    }
    return result;
};

方法2 单调栈法

维护单调递增的单调栈,栈里内容是元素下标。第一个元素先入栈。

  1. 如果遇到比栈顶小的元素,则入栈
  2. 如果等于栈顶元素,出栈,入栈,替换当前元素
  3. 如果大于栈顶元素,则需要计算结果,表示遇到凹槽了,可以接水了
    • top出栈
    • 宽度等于 索引相减-1
    • 高度等于 当前元素是凹槽的右边元素,top是凹槽,top的下一个元素是凹槽的左边,所以高度等于 min(左侧,右侧) - height
    • 如果结果大于0,则相加。
var trap = function(height) {
    const len = height.length;
    const stack = [0];
    let result = 0;
    for (let i = 1; i < len; i++) {
        const val = height[i];
        while (stack.length && height[stack[stack.length-1]] < val) {
            let top = stack[stack.length-1];
            const right = val;
            stack.pop();
            if (stack.length) {
                const mid = height[top];
                // left是当前栈顶元素
                top = stack[stack.length - 1];
                const left = height[top];
                const h = Math.min(left, right) - mid;
                const w = i - top - 1;
                result += h * w;
            }
        }
        stack.push(i);
    }
    return result;
}