单调栈的三个问题
一上午做了三道单调栈的问题,算是小摸了一下单调栈的套路,虽然很难想,掌握了规律还是很好做出来的!
84,柱状图中最大的矩形,hard
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。

图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3] |
题解
先考虑暴力解法,也就是对数组的每个高度来说,向左找、向右找到第一个比它小的高度,即可确定矩阵的宽,很容易就得到最大面积。
但这样时间复杂度比较高,每个元素都要循环两遍,为O(N2),考虑怎么优化呢?
以空间换时间,对每个元素向后找比它小的,才能计算面积,符合【栈】先进后出的规则。用一个单调栈来记录下标(高度可以用下标来表示),这样就能很清楚的确定形成矩形的宽了。
如果当前的高度比它之前的高度(栈顶元素的高度)严格小于的时候,就可以直接确定以栈顶元素为高的矩形的面积,向左回退的时候,其实就可以当中间这些柱形不存在一样。
还有一个细节,为了确定数组两端元素的高为矩形的面积,在数组两端添加比 1 小的元素,这样就始终保证栈非空。
下面图帮助理解

代码
class Solution { |
739,每日温度,medium
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
题解
首先想到暴力解法,两层for循环,搜索每个元素后面比它大的值,就得到每个元素需要等待的天数。
此题目的是向右找到比当前索引对应的高度 高 的 第一个柱体,所以需要维护一个递减栈。
遍历数组元素,当前索引对应的高度比栈顶元素对应高度 大,即弹出栈顶并将索引差记录到结果数组。

代码
class Solution { |
42,接雨水,hard
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] |
题解
依然是遍历到右面不比自己低的柱子时,会形成坑接住雨水。所以采用【单调栈】。
维护一个递减的单调栈,仍记录下标,栈中每个元素表示接雨水的【左侧柱体】。
遍历数组元素,当前元素对应高度大于栈顶元素对应高度,则能形成雨水,栈顶元素弹出,并记录其索引,去除栈中元素对应高度重复的元素(为了不重复计算)。计算雨水时,仍然采用高度*宽度,宽度 = 当前遍历索引 - 栈顶索引 - 1;高度 = 两侧柱体取最矮的 - 上面记录的弹出栈的索引对应的高度。因为以记录的柱体作为左侧柱体的雨水已经计算过了!
代码
class Solution { |
总结
这三道题都可以用单调栈来解,各自也有不同的解法。总的来说,【单调栈】以空间换时间,使暴力解法的左右两次循环变为朝一个方向循环一次。在向左。
还有一个细节,就是记录下标,而不是将元素值入栈,这是为了方便计算间隔来计算面积,写代码时要提醒自己。