给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
首先定义:
对柱形图中的一个矩形i来说,我们计算以heights[i]为高的矩形的最大面积时,以i为起点,向左找到第一个小于heights[i]的矩形j,向右找到第一个小于heights[i]的矩形k。(j,k)区间,也即[j+1,k-1]所围成的以heights[i]的矩形就是i的最大矩形。当然,这个矩形面积不一定是整个柱形图中的最大矩形,但一定是i的最大矩形,因为i是[j+1,k-1]区间内最短的一个,如果这个区间内有某个矩形z的高heights[z]小于heights[i],则区间所围成的矩形最大面积应该以heights[z]为高,因为z才是真正的短板。
因此,如果我们要计算整个柱形图中的最大面积,其实只要找到所有的以i(i=0....n-1)的高为高的最大矩形,在里面选一个就好了。
实际操作上可以对每个矩形都向左找一个上界,向右找一个下界,但这样时间复杂度实际上是处于O(n^2)。
因此可以考虑用一个单调递增的栈,栈中存储的是矩形的索引。
首先为什么要选取一个单调递增栈呢?因为如果采取单调递增栈,则对栈中任意一个元素i来说,i以前入栈的元素一定都比它矮,也就是说我们不用去寻找上界,它的上界一定就是第i-1个元素。
然后为什么要存储矩形的索引呢?因为根据索引我们既可以马上得到它的高heights[i],也可以马上得到它的宽,只要用索引加减就可以了。
计算流程:
1).最开始入栈-1(-1的作用是当栈中只有一个矩形时,它的上界就是-1,不然栈中只有这一个矩形的话上界不方便计算)。
2).如果当前矩形i的高>=heights[aux.top()],则入栈。这是为了保持栈的单调递增性质。
3).如果当前矩形i的高<heights[aux.top()],则将aux.top()出栈,对出栈的这个元素j来说,他在栈中的前一个元素a一定是小于它的,所以一定是它的上界。现在的这个元素i的高也一定是小于它的,所以是它的下界,因此以aux.top()为高的最大矩形的上下界就已经确定下来为(a,i),也就是[a+1,i-1]了,对这个矩形j来说,它的最大面积为heights[j]*(i-1-(a+1)+1)。计算得到这个面积area后,将他与max作对比并做记录。继续与现在的aux.top()和i做对比,出栈直到heights[aux.top()]<heights[i]时,在将i入栈。
4)循环这个过程。当然,如果说矩形的最后几个元素高都是递增的,最后会发现栈中除-1外还有元素,因此在最开始我们就在heights中加入一个0,这个0的作用就是用于最后对付栈未空的情况,因为0一定可以作为矩形们的下界。
代码:
class Solution { public: int largestRectangleArea(vector<int>& heights) { if(heights.size()==0) return 0; stack<int> aux; int max=INT_MIN; aux.push(-1); heights.push_back(0); for(int i=0;i<heights.size();i++) { if(aux.top()==-1||heights[aux.top()]<=heights[i]) aux.push(i); else { while(aux.top()!=-1&&heights[aux.top()]>heights[i]) { int h=heights[aux.top()]; aux.pop(); int area=h*(i-aux.top()-1); if(area>=max) max=area; } aux.push(i); } } if(max<=0) return 0; return max; } };