题目描述:
给定非负整数数组 heights ,数组中的数字用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例:
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10
解法: 单调栈
根据木桶效应,在相邻的若干矩形中,能够构成的最大矩形的高取决于这些矩形中最矮的那个。 因此在一片相邻的矩形中,只要找到最矮的,其他矩形的高就可以不用考虑。
因此可以想到(可能想不到)使用单调栈的方法,构建一个单调递增的栈。如果遇到的柱子高度比栈顶柱子的高度要高,就直接入栈,维持栈的单调递增特性就好。
(为什么是单调递增栈? 因为这样的话,后入栈的元素都会更大,在计算矩形面积的时候,从栈中间到栈顶围成的矩阵高度就是栈中间的高度,而宽度就是两个下标的差值,直接相乘就可以算出面积。)
核心是 遇到的柱子高度如果比栈顶的柱子高度要低,那么如何操作。
根据单调栈的思想,如果遇到的柱子比栈顶柱子低,就需要对栈中存在的元素进行计算,出栈,直到栈空或者栈顶元素比新遇到的柱子高。 这样才可以维持栈的单调性。
因此, 整个方法的主要步骤是:
- 新遇到的柱子比栈顶柱子高,或者此时栈为空,新柱子的下标直接入栈;
- 新遇到的柱子更低,栈开始弹出元素,直到栈为空,或者直到遇到栈顶的柱子比新遇到的柱子要高。 在栈不断弹出元素的过程中,就可以针对每个弹出的元素(也就是柱子下标),针对每个柱子计算以这个柱子为高的面积。 这个面积的计算可能稍微有一些绕,首先,这个面积的高就是从栈中弹出的柱子的高,但是**这个面积的宽并不是新柱子与这个柱子下标的差值。**新柱子到这个柱子下标的差值可能只是面积的右边的一部分,这个柱子左边可能还有比它高的柱子! 但是前面提到是单调递增栈,左边的不是都比它更矮吗?这里很关键的一点是:左边比它高的柱子压根没进栈 ,因为要维护栈的单调性! 因此,这个矩形的宽其实是这个柱子在栈中左侧的柱子(此时是栈顶)下标与新柱子下标的差值。 我们用i来表示新遇到的柱子下标,cur表示栈顶柱子下标,pre表示被cur压在底下那个柱子(次栈顶),在计算以cur柱子为高的面积时,宽其实是i-(pre+1)=i-pre-1。
tips:
- 可以一开始在栈中放入一个-1,表示栈空,简化过程。
- 可以在柱子列表尾部加入一个-1,这样在遍历到尾部时,可以一把子把前面的算完。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> s;
// 通过一个单调栈实现
int n=heights.size();
if(n==0) return 0;
heights.emplace_back(-1);
s.push(-1);
// 放一个-1表示栈底
int res=0;
for(int i=0; i<=n; i++){
// s存的是height的下标
while(s.top()!=-1 && heights[i]<heights[s.top()]){
// 如果等于-1,遇到栈底,栈为空,直接push
int height=heights[s.top()];
s.pop();
int width=i-(s.top()+1);
res=max(res, height*width);
}
s.push(i);
}
return res;
}
};