题目
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
思路
- 暴力法:
- 有下述事实:最大矩形的高度一定等于某个柱子的高度
- 遍历每个柱子(对于柱子 i i i,其高度为 h e i g h t s [ i ] heights[i] heights[i]),计算当矩形高度等于 h e i g h t s [ i ] heights[i] heights[i] 时所能构成的矩形的最大面积
- 找到第 i i i 个柱子向左向右所能延申的最大宽度,即找到左边第一个小于 h e i g h t s [ i ] heights[i] heights[i] 的柱子(假设索引为 l l l)和右边第一个小于 h e i g h t s [ i ] heights[i] heights[i] 的柱子(假设索引为 r r r)
- 此时的最大矩形面积: h e i g h t s [ i ] ∗ ( r − l − 1 ) heights[i]*(r-l-1) heights[i]∗(r−l−1)
- 如何更快的找到每个柱子左边/右边第一个小于它的柱子:
- 单调栈
- 本题考的基础模型其实就是:在一维数组中对每一个数找到第一个比自己小的元素。这类“在一维数组中找第一个满足某种条件的数”的场景就是典型的单调栈应用场景。
- 从左到右遍历数组,如果当前遍历到的数大于栈顶元素,则将当前遍历到的数入栈;如果当前遍历到的数小于等于栈顶元素,不断的将栈顶元素出栈直到栈顶元素小于该数。保证了栈中元素的单调性。
- 对于本题,栈中存放索引,栈中索引对应的数组中的数是单调的。每次遍历时,栈顶的数即为待求的第一个小于自己的索引;从左到右和从右到左分别遍历即可得到每个柱子左边/右边第一个小于它的柱子
- 优化:上面是左到右右到左遍历两次,可以优化到一次
- 假设当前遍历到 i i i,栈顶元素不满足条件需要出栈,此时出栈的元素对应的柱子的右边第一个小于等于它的柱子即为 i i i 对应的柱子
- 注意到上述方法找到的不是第一个小于它的柱子,而是第一个小于等于它的柱子。但这对最后的结果没有影响:由于等高数列的最后一个数的左边界和中间算错的部分的左边界相同,因此会覆盖掉中间的错误部分,计算面积的时候是对的。
- 单调栈
代码
class Solution {
public:int largestRectangleArea(vector<int>& heights) {// int n = heights.size();// stack<int> stk;// stk.push(-1);// vector<int> l2r;// vector<int> r2l;// for(int i = 0; i < n; i++){// while(stk.top() != -1 && heights[i] <= heights[stk.top()]){// stk.pop();// }// l2r.push_back(stk.top());// stk.push(i);// }// while (!stk.empty()){// stk.pop();// }// stk.push(n);// for(int i = n-1; i >= 0; i--){// while(stk.top() != n && heights[i] <= heights[stk.top()]){// stk.pop();// }// r2l.push_back(stk.top());// stk.push(i);// }// int ret = 0;// for(int i = 0; i < n; i++){// ret = max(ret, heights[i]*(r2l[n-1-i]-l2r[i]-1));// }// return ret;int n = heights.size();stack<int> stk;stk.push(-1);vector<int> l2r;int ret = 0;for(int i = 0; i < n; i++){while(stk.top() != -1 && heights[i] <= heights[stk.top()]){ret = max(ret, heights[stk.top()]*(i-l2r[stk.top()]-1));stk.pop();}l2r.push_back(stk.top());stk.push(i);}while(stk.top() != -1){ret = max(ret, heights[stk.top()]*(n-l2r[stk.top()]-1));stk.pop();}return ret;}
};