84. Largest Rectangle in Histogram
Description:
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.
For example,
Given heights = [2,1,5,6,2,3]
,
return 10
.
Solution:
一、题意理解
题意很简单,就是在直方图中找到最大的矩形。
二、分析
1、直接暴力的思路是对于每一个子直方图,找到其中最低的高度,然后求面积,最后可以得出最大的矩形面积。总共有n^2个子直方图,总时间复杂度为O(n^3)。
2、进一步思考,可以从每一个bar往两边走,以自己的高度为标准,直到两边低于自己的高度为止,然后用自己的高度乘以两边的走的宽度得到矩阵面积。时间复杂度为O(n^2)。
3、思考用DP的方法来做。如果已知直方图是升序的,应该怎么做?
比如{2, 4, 6, 7, 9}
结果就是max(2*5, 4*4, 6*3, 7*2, 9*1)。
4、直方图显然不能保证都是升序的,那么我们可以构造出这样一个升序序列,按照以上方法求解。这个栈从底向上的高度是依次递增的。如果遇到当前bar高度比栈顶元素低,那么就出栈直到栈顶元素低于当前bar,出栈过程中检测前面满足条件的矩阵的大小:
- 如果栈已经为空,说明到目前为止所有元素(当前下标元素除外)都比出栈元素高度要大(否则栈中肯定还有元素),所以矩阵面积就是高度乘以当前下标i。
- 如果栈不为空,那么就是从当前栈顶元素的下一个到当前下标的元素之间都比出栈元素高度大(因为栈顶元素第一个比当前出栈元素小的),所以矩阵面积就是高度乘以当前下标i减栈顶元素再减1。
5、使用<stack>来构造,比如A = {2, 1, 5, 6, 2, 3},设 i 为数组下标,定义 stack<int> s;
(1)i = 0,A[i] = 2,栈为空,i = 0入栈。s = {0},res = 0。
(2)i = 1,A[i] = 1,A[ s.top() ] = 2> A[i] = 1,不满足升序,无法入栈,因此进入循环:
记录h = A[ s.top() ] = 2,将s.top()弹出,计算cur_res = h * i = 2 * 1 = 2,res = max(res, cur_res) = 2。
栈为空,退出循环。
将 i = 1入栈,s ={1},res = 2。
(3)i = 2,A[i] = 5,A[ s.top() ] = 1< A[i] = 5,满足升序,将 i = 2 入栈。s = {1, 2},res = 2。
(4)i = 3,A[i] = 6,A[ s.top() ] = 5< A[i] = 6,满足升序,将 i = 3 入栈。s = {1, 2, 3},res = 2。
(5)i = 4,A[i] = 2,A[ s.top() ] = 6> A[i] = 2,不满足升序,无法入栈,因此进入循环:
记录h = A[ s.top() ] = 6, 将s.top()弹出,计算cur_res = h * width = h * (i - s.top() -1 ) = 6 * (4 - 2 - 1) = 6,res = max(res, cur_res) = 6。
s ={1, 2},res = 6。栈不为空。A[ s.top() ] = 5> A[i] = 2,不满足升序,循环继续:
记录h = A[ s.top() ] = 5, 将s.top()弹出,计算cur_res = h * width = h * (i - s.top() -1 ) = 5 * (4 - 1 - 1) = 10,res = max(res, cur_res) = 10。
s = {1},res = 10。栈不为空。A[ s.top() ] = 1< A[i] = 2,满足升序,退出循环。
将 i = 4入栈,s = {1, 4},res = 10。
(6)i = 5,A[i] = 3,A[ s.top() ] = 2 < A[i] = 3,满足升序,将 i = 5入栈。s = {1, 4, 5},res = 10。
(7)这里由于栈不为空,需要再处理一次,为了简便,我们最开始在直方图的最后插入一个权值为0的bar,即A = {2, 1, 5, 6, 2, 3, 0}。这样能保证最后栈的有效数据能被处理完成。
(8)i = 6,A[i] = 0,A[ s.top() ] = 3 > A[i] = 0,不满足升序,无法入栈,因此进入循环:
记录h = A[ s.top() ] = 3, 将s.top()弹出,计算cur_res = h * width = h * (i - s.top() -1 ) = 3 * (6 - 4 - 1) = 3,res = max(res, cur_res) = 10。
s = {1, 4},res = 10。栈不为空。A[ s.top() ] = 2 > A[i] = 0,不满足升序,循环继续:
记录h = A[ s.top() ] = 2, 将s.top()弹出,计算cur_res = h * width = h * (i - s.top() -1 ) = 2 * (6 - 1 - 1) = 8,res = max(res, cur_res) = 10。
s = {1},res = 10。栈不为空。A[ s.top() ] = 1> A[i] = 0,不满足升序,循环继续:
记录h = A[ s.top() ] = 1, 将s.top()弹出,计算cur_res = h * width = h * (i - s.top() -1 ) = 1 * 6 = 6,res = max(res, cur_res) = 6。
栈为空。 退出循环。将 i = 6入栈,s = {6},res = 10。
(8)遍历完成,输出res = 10即是最优解。
6、时间复杂度O(n),代码如下:
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int res = 0;
stack<int> s;
heights.push_back(0);
int size = heights.size();
for(int i = 0; i < size; ++i) {
while(!s.empty() && heights[s.top()] >= heights[i]) {
int h = heights[s.top()];
s.pop();
int cur_res = h * (s.empty() ? i : (i - s.top() - 1));
res = max(res, cur_res);
}
s.push(i);
}
return res;
}
};
二、参考资料
- http://blog.youkuaiyun.com/makuiyu/article/details/44857459
- http://www.cnblogs.com/ganganloveu/p/4148303.html