在直方图中找到面积最大的矩形的面积,第一眼看到此题就觉得和LeetCode的42题接雨水好像,最后也的确如此。原题如下
Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height =[2,1,5,6,2,3]
.
The largest rectangle is shown in the shaded area, which has area =10
unit.
Example:
Input: [2,1,5,6,2,3] Output: 10
一、栈
最大的矩形的高一定是直方图中的某一个竖条,宽一定是相邻的所有不低于此值的竖条的集合。问题的关键就在于界定这个集合的范围,这个集合的左边起点一定是左边最近的低于此值的竖条(或者最左边界),右终点一定是右边最近的低于此值的竖条(或者最右边界)。我们可以用一个栈来记录,当栈顶元素小于当前扫描到的竖条时,还未发现右终点,直接把竖条压入栈,于是栈中的元素是递增的(从另一个角度说递减的竖条非常简单根本用不到栈来记录),当栈顶元素大于当前扫描到竖条时,说明右终点到了,并且左起点是栈中第二个元素(因为栈中元素是递增的),宽度恰好为i-pre.top()-1,之后不断把大于当前竖条i的元素取出计算,取完后吧竖条i压入栈,继续向后扫描。
这里出现了两个烦人的特殊情况--最左边界和最右边界,我们可以通过一些手段不必每次都判断有没有遇到边界,通过把-1先行入栈可以确保i-pre.top()-1即使在栈中没有竖条时也能计算出矩形的宽度(此时起点正好是左边界),通过给原数组末尾补0既不影响结果又解决了最后可能栈内有剩余的麻烦。优化后的代码如下:(此优化只是使代码更简洁,运行效率和优化前没有区别)
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
heights.push_back(0);
stack<int> pre;
pre.push(-1);
int n = heights.size(),temp;
int result = heights[0];
for (int i = 0;i < n;i++){
while(pre.top()!=-1 && heights[i]<heights[pre.top()]){
temp = pre.top();
pre.pop();
result = max(result,heights[temp]*(i-pre.top()-1));
}
pre.push(i);
}
return result;
}
};
也可以用数组模拟栈,思路完全一样,可以减少栈的开销,略微提升一点效率
class Solution {
public:
int largestRectangleArea(vector<int>& height) {
height.push_back(0);
int len = height.size(),res = 0, cur=1;
int s[len+1]={0};
s[0]=-1;
for(int i=1;i<len;i++){
while(cur && height[i]<height[s[cur]])
res = max(res, height[s[cur]] * (i-s[--cur]-1));
s[++cur]=i;
}
return res;
}
};
感觉这道题还是蛮经典的,刚在牛客网也看到了它的变种,附上链接如下https://www.nowcoder.com/question/next?pid=8537283&qid=141062&tid=22238950