一、单调栈的概念
从栈底元素到栈顶元素呈单调递增或单调递减,栈内序列满足单调性的栈。
二、单调栈算法举例
LeetCode_84柱状图中最大的矩形(https://leetcode-cn.com/problems/largest-rectangle-in-histogram/)
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以下是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
![3d0b6cf33736ea97d0195bf4c0860e39.png](https://i-blog.csdnimg.cn/blog_migrate/7548a32a54c7e24a8d7fadc46f3de54a.jpeg)
按照单调栈的思路,递增时入栈,其余时计算面积并出栈,直到继续递增时入栈。面积将通过计算当前位置和栈顶元素所在位置的宽度查,再乘上元素的height值的思路来做这题:
class Solution {
public int largestRectangleArea(int[] heights) {
// 两个栈,一个存位置,一个存值
Stack<Integer> posStack = new Stack<>();
Stack<Integer> valueStack = new Stack<>();
posStack.push(-1);
valueStack.push(-1);
int maxArea = 0;
for (int i = 0; i <= heights.length; i++) {
// 当前值比栈顶大,即压栈
if (i < heights.length && heights[i] >= valueStack.peek()) {
posStack.push(i);
valueStack.push(heights[i]);
}
else {
// 已到头了,挨个向前把每个面积都算一下
if (i >= heights.length) {
while (posStack.peek() != -1) {
int value = valueStack.peek();
valueStack.pop();
posStack.pop();
int area = value * (i - posStack.peek() - 1);
maxArea = area > maxArea ? area : maxArea;
}
}
// 未到头时,当前值比栈顶小的,则向前计算面积,直到比栈顶大,压栈
else {
while (heights[i] < valueStack.peek()) {
int value = valueStack.peek();
valueStack.pop();
posStack.pop();
int area = value * (i - posStack.peek() - 1);
maxArea = area > maxArea ? area : maxArea;
}
posStack.push(i);
valueStack.push(heights[i]);
}
}
}
return maxArea;
}
}
这道题说起来花了不少时间,从理解思路,到实际写代码,写代码时有两个用例又没通过,再重新调试对了思路。直到最后现在的结果。
![21cca915b6d2e64896d565a27a9c0b8a.png](https://i-blog.csdnimg.cn/blog_migrate/136c564cb1ecbab7ac10e2bd33f502e2.png)
其实可以做一些代码优化,少一个栈空间,代码做下抽去方法的精简,但其实我觉得没太大的必要,可以看下优化的:
class Solution {
public int largestRectangleArea(int[] heights) {
// 初始化栈,存位置-1,压底
Stack<Integer> posStack = new Stack<>();
posStack.push(-1);
// 最大面积
int maxArea = 0;
for (int i = 0; i <= heights.length; i++) {
// 当前值比栈顶大,即压栈
if (i < heights.length && heights[i] >= (posStack.peek() == -1 ? -1 : heights[posStack.peek()])) {
posStack.push(i);
}
else {
// 已到头了,挨个向前把每个面积都算一下
if (i >= heights.length) {
while (posStack.peek() != -1) {
maxArea = getMaxArea(posStack, maxArea, i, heights[posStack.peek()]);
}
}
// 未到头时,当前值比栈顶小的,则向前计算面积,直到比栈顶大,压栈
else {
while (heights[i] < (posStack.peek() == -1 ? -1 : heights[posStack.peek()])) {
maxArea = getMaxArea(posStack, maxArea, i, (posStack.peek() == -1 ? -1 : heights[posStack.peek()]));
}
posStack.push(i);
}
}
}
return maxArea;
}
private int getMaxArea(Stack<Integer> posStack, int maxArea, int i, int height) {
posStack.pop();
int area = height * (i - posStack.peek() - 1);
return area > maxArea ? area : maxArea;
}
}
三、单调栈整体思路
单调栈主要运用在给你一个数组序列,在遍历数组时,需要向前查看元素的这种情况,此时将前面的元素入栈,形成单调栈。具体再结合题目的用意,加以利用。
整体思路还是在于自己看如何利用单调栈,有一定的难度。
其它单调栈题解:
工藤新木:柱状图中最大的矩形(单调栈)
工藤新木:每日温度(单调栈)
工藤新木:下一个更大元素 II(单调栈)
工藤新木:最大矩形(单调栈)
工藤新木:接雨水(单调栈)
工藤新木:股票价格跨度(单调栈)
工藤新木:最大宽度坡(单调栈)