js单调栈

设计力扣题目:

    1. 每日温度
  1. 496.下一个更大元素 I
  2. 503.下一个更大元素II
    1. 接雨水
  3. 84.柱状图中最大的矩形
    关于单调栈问题:它主要是用来解决如何求解一串数组中每个元素,向后(向前)的第一个大于它的元素的位置。
    关于这个问题,可以使用时间复杂度n*n的两层暴力for循环,我们是用单调栈的目的是使两层for循环变成一层。

我们可以定义一个stack数组用来模拟栈。采用头插法(方便取元素)模拟进出栈。for循环遍历每一个元素,每个元素与栈顶进行比较。只要其小于栈顶就入栈,大于栈顶元素就把栈顶元素去除同时记录当前元素为栈顶元素的第一个大于元素。然后直到当前元素比栈顶元素小时结束。

  1. 每日温度

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

定义一个长度为气温列表长度的结果数组res,栈的值就是第一个最大元素的下标(注意一定是下标)。定义一个空栈stack。开始for循环。然后当前元素与栈顶元素,不断出入栈。

完整代码:

var dailyTemperatures = function(temperatures) {
    let res = Array(temperatures.length).fill(0)
    let stack = []
    for(let i=0;i<temperatures.length;i++){
        while(stack.length&&temperatures[i]>temperatures[dp[0]]){
            res[stack[0]]=i-stack[0]//两下标相减记录下距离
            dp.shift()//栈顶出栈
        }
            dp.unshift(i)//最后当前元素入栈
    }
    return res
};

496.下一个更大元素 I

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2
确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

这个题目本质没变。只是绕了一个圈子。
大不了就是把nums1元素在nums2中的下标记录下来,最后按最大温度的方式求,再把对应下标一个一个提出来(对就是这么暴力,没啥其他办法)

完整代码:

var nextGreaterElement = function(nums1, nums2) {
    let indexArr = []
    for(let i=0;i<nums1.length;i++){
        indexArr.push(nums2.indexOf(nums1[i]))
    }

    let res = Array(nums2.length).fill(-1)
    let dp = [0]
    for(let i=0;i<nums2.length;i++){
        while(nums2[i]>nums2[dp[0]]){
            res[dp[0]]=nums2[i]
            dp.shift()
        }
        if(nums2[i]<=nums2[dp[0]]||dp.length===0){
            dp.unshift(i)
        }
    }
    let resArr = []
    for(let i=0;i<indexArr.length;i++){
        resArr.push(res[indexArr[i]])
    }
    return resArr
};

503.下一个更大元素II

这题由距离变成了值,反而更简单了,但是加了一个看似很难的条件:可以循环。其实可以想一想,循环最多循环一轮,大不了把数组乘以二为2n长度,求前n个就行。

完整代码:

var nextGreaterElements = function(nums) {
    let stack = []
    let res =Array(nums.length*2).fill(-1)
    let newArr = [...nums,...nums]
    for(let i=0;i<newArr.length;i++){
        while(stack.length&&newArr[i]>newArr[stack[0]]){
            res[stack[0]]=newArr[i]
            stack.shift()
        }
        stack.unshift(i)
    }
    return res.slice(0,nums.length)
};
  1. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
关于接雨水就有一点小难度,主要设计单调栈的进阶用法:那就是栈顶下第一个元素就是当前栈顶元素的左边第一个大于它的元素,这样就可以拿到左右两边的第一个最大。

接下来就是计算面积的公式。
右边减去左边-1 * 当前高度即可

最后设置个值一直累加面积即可。

完整代码:

var trap = function(height) {
     let len = height.length
     if(len<=2) return 0
     let sum=0
     let stack =[]
     for(let i=0;i<len;i++){
         while(stack.length&&height[i]>height[stack[0]]){
             let mid = height[stack[0]]
             stack.shift()
             if(stack.length!==0){
                 let h = Math.min(height[stack[0]],height[i])-mid
                 let w = i-stack[0]-1
                 sum+=h*w
             }
         }
         stack.unshift(i)
     }
     return sum
};

84.柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。

最大面积与接雨水相反要求最小。
大于改成小于就大差不差

完整代码:

var largestRectangleArea = function(heights) {
     let maxArea = 0;
    const stack = [];
    heights = [0,...heights,0]; // 数组头部加入元素0 数组尾部加入元素0
    for(let i = 0; i < heights.length; i++){ // 只用考虑情况一 当前遍历的元素heights[i]小于栈顶元素heights[stack[stack.length-1]]]的情况
        while(heights[i] < heights[stack[stack.length-1]]){// 当前bar比栈顶bar矮
            const stackTopIndex = stack.pop();// 栈顶元素出栈,并保存栈顶bar的索引
            let w = i - stack[stack.length -1] - 1;
            let h = heights[stackTopIndex]
            // 计算面积,并取最大面积
            maxArea = Math.max(maxArea, w * h);
        }
        stack.push(i);// 当前bar比栈顶bar高了,入栈
    }
    return maxArea;
};
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值