单调栈 求解下一个更高温度问题,简单易懂

本文大部分转载自 https://blog.csdn.net/weixin_42784951/article/details/88963758

只是对答案进行了描述说明

==============================================

适用问题:

要知道单调栈的适用于解决什么样的问题,我们首先需要知道单调栈的作用。单调栈分为单调递增栈和单调递减栈,通过使用单调栈我们可以访问到下一个比他大(小)的元素(或者说可以)。也就是说在队列或数组中,我们需要通过比较前后元素的大小关系来解决问题时我们通常使用单调栈。下面我们通过简单介绍单调减栈和单调增栈问题来进一步说明使用单调栈处理问题的过程。

首先说一下单调递减栈

单调递减栈: ①在一个队列中针对每一个元素从它右边寻找第一个比它大的元素
           ②在一个队列中针对每一个元素从它左边寻找第一个比它大的元素(从后往前遍历)


其实 1 和 2 只是从两个不同的角度来分析问题,但是它解决问题的过程是一样的。并且在寻找的过程中我们都需要对栈内的元素进行一定的处理。我们通过介绍一个简单的例子来说明这个问题。
假如有一个地方三月三十一号要传授武林秘籍,大家要在这一天之前来这里排好队等3月31号能够学到东西。来了很多武林高手,但是这个地方的人要根据人来的先后顺序教,先来的学的武功就高深,来的越靠后学的就越差,但是能保证只要来就能学到。假如他们一开始有一个初始的排队顺序和武力水平如下所示,大家可以按照这个初始顺序从前往后学习。

在这里插入图片描述

但是问题来了,武力值高的肯定愿意先学习到高深一点的武功啊,那我就找到前面武力值低的人说:“你别学了,我叫你一门武功比那里学的好”,武力值低的那一个一听就答应了,从它哪里学了一门武术高高兴兴的走了,这样武功高的那个就排在了前面。下面我们来从前往后模拟一下过程:

(1)首先来的是炮灰甲,炮灰甲一看,OK,栈内没有人就可以先排在第一位
(2)然后扫地僧来了,扫地僧教给了炮灰甲自己的功夫(炮灰甲的师傅为扫地僧),然后扫地僧让炮灰甲离开自己排在第一位
(3)然后杨过过来,看到前面是扫地僧,自己打不过只能老老实实站到队里
(4)后面一直来人(如3)------ 直到张三 对里面站的是   扫地僧 杨过 慕容复 张三   
(5)然后张无忌来了,他首先看到前面是张三,张无忌教给张三自己的功夫并让他离开(张三师傅为张无忌),
        然后张无忌再往前看到了慕容复(过程如上所示)------------直到遇到扫地僧,OK,自己打不过,
        老老实实的占到了后面      —现在队里有扫地僧,张无忌     ----杨过,慕容复,张三的师傅为张无忌
(6)柯镇恶来了,打不过,站到后面就好了
(7)然后乔峰来了把柯镇恶和张无忌打发走,   —现在队里有扫地僧,乔峰     ----张无忌,柯镇恶的师傅为乔峰
(8)然后李四来了,打不过乔峰,只能站到了最后。
(9)第二天扫地僧,乔峰,李四成功学到了武林秘籍


现在看他们分别学到武功对应如下图:

在这里插入图片描述
这样我们就处理完了所有过程。因此单调栈的时间复杂度为O(n),在比较时对出栈的元素有一个处理(例如扫地僧让炮灰甲走的时候教给他自己的武功),另外最后留在站内的元素有一个统一的处理(扫地僧,乔峰,李四成功学到了武林秘籍)。
 伪代码如下所示:

对于第i个到来的人:
	每当队里面有人并且打不过自己的时候:
		让这个人离开并交给他自己的武功
	自己入队			

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

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

下面就是 JavaScript 编写的答案

/**
 * @param {number[]} T
 * @return {number[]}
 */
var dailyTemperatures = function(T) {
        var length = T.length;
        var ans =new Array(length).fill(0);//每一个王者,遇到下一个王者的距离
        var stack = [];//存的是元素的位置序号,不是元素本身,存的是相对强者,王者队伍
        for (var i = 0; i < length; i++) {
            //while 循环,就是把弱小的元素踢掉,可能一下子会踢掉好几个
            while (stack.length>0 && T[i] > T[stack[stack.length-1]]) {
                //var prevIndex = stack.pop();
                var prevIndex = stack[stack.length-1]
                stack.pop()
                //所以被踢掉的弱者,遇到强者的距离为:当前强者与这个弱者的距离差
                ans[prevIndex] = i - prevIndex;
            }
            //上面踢掉之后,自己顶上
            stack.push(i);
        }
        return ans;
};

【力扣题目】如果数组是单调递增或单调递减的,那么它是单调的。

如果对于所有 i <= j,A[i] <= A[j],那么数组 A 是单调递增的。 如果对于所有 i <= j,A[i]> = A[j],那么数组 A 是单调递减的。

当给定的数组 A 是单调数组时返回 true,否则返回 false。

示例 1:
输入:[1,2,2,3]
输出:true

示例 2:
输入:[6,5,4,4]
输出:true

示例 3:
输入:[1,3,2]
输出:false

示例 4:
输入:[1,2,4,5]
输出:true

示例 5:
输入:[1,1,1]
输出:true
 

JavaScript编写的答案如下

/*判断一个数组是否单调*/
function ddcheck(arr){
    let asc=[]//单调递增
    let desc=[]//单调递减
    for(let i=0;i<arr.length;i++){
        var node=arr[i];
        if(asc.length==0 || node>=asc[asc.length-1]){
            asc.push(node)
        }
        if(desc.length==0 || node<=desc[desc.length-1]){
            desc.push(node)
        }
    }
    if(asc.length===arr.length || desc.length===arr.length){
        return true;
    }else{
        return false;
    }
}

/*test case*/
console.log(ddcheck([1,2,3]))//true
console.log(ddcheck([1,3,2]))//false
console.log(ddcheck([4,2,1]))//true
console.log(ddcheck([2,2,3]))//true
console.log(ddcheck([1,1,1]))//true

【力扣* 难】柱状图最大矩形面积 (https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

利用单调递增栈来求解

const largestRectangleArea = (heights) => {
  let maxArea = 0
  const stack = []
  heights = [0, ...heights, 0]         
  for (let i = 0; i < heights.length; i++) { 
    while (heights[i] < heights[stack[stack.length - 1]]) { // 当前bar比栈顶bar矮
      const stackTopIndex = stack.pop() // 栈顶元素出栈,并保存栈顶bar的索引
      maxArea = Math.max(               // 计算面积,并挑战最大面积
        maxArea,                        // 计算出栈的bar形成的长方形面积
        heights[stackTopIndex] * (i - stack[stack.length - 1] - 1)
      )
    }
    stack.push(i)                       // 当前bar比栈顶bar高了,入栈
  }
  return maxArea
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值