题目来源:https://leetcode.com/problems/largest-rectangle-in-histogram/
问题描述
Hard
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
------------------------------------------------------------
题意
给定一组数列组成直方图,求直方图中的最大矩形面积。
------------------------------------------------------------
思路
单调栈。
首先考虑当直方图的高度是从左往右递增的,我们应该如何解决这个问题呢?当直方图的高度递增时,例如[1,2,2,3,5,6],我们可以很容易求出每个高度数值(1,2,3,5,6)对应的矩形面积(1*6, 2*5, 3*3, 5*2, 6*1)。
那当直方图的高度不是单调递增时候怎么求解呢?答案是用单调栈维护一个从栈底到栈顶递增的直方图。具体做法是,单调栈中存储一个类,类的两个属性分别是高度height和这个高度上连续的直方体个数cnt。在遍历直方图的过程中,为了维护栈的单调性,如果当前直方体的高度h大于等于单调栈的栈顶高度,则该直方体入栈;如果h小于单调栈的栈顶高度,则单调栈循环弹出直方体,每弹出一个高度数值的连续直方体集合,就更新一下最大面积,直到栈空或h大于等于新的栈顶,此时要将等于弹出个数的高为h的连续直方体集合入栈。
由于实际算法执行的时候可以记录连续多个等高的直方体的个数而不用逐个弹出栈顶的每一个直方体,因此算法的时间复杂度可能是O(n).
下面用[2,1,5,6,2,3]的例子来演示这个算法。
Step |
单调栈 |
当前弹出面积 |
最大面积 |
0 |
[] <= 2 |
null |
0 |
1 |
[2] <= 1 |
2 |
0 |
2 |
[1,1] <= 5 |
null |
2 |
3 |
[1,1,5] <= 6 |
null |
2 |
4 |
[1,1,5,6] <= 2 |
6 |
2 |
5 |
[1,1,5,5] <= 2 |
10 |
6 |
6 |
[1,1,2,2,2] <= 3 |
null |
10 |
7 |
[1,1,2,2,2,3] |
null |
10 |
8 |
[1,1,2,2,2,2] |
3 |
10 |
9 |
[1,1,1,1,1,1] |
8 |
10 |
10 |
[] |
4 |
10 |
注:实际代码中单调栈存储的是(高度,连续等高的直方体个数)对象,例如单调栈[1,1,5,6]被存储为[(1,2), (5,1), (6,1)]. 同时在多对元素出栈时单调栈的情况也与表中演示的(第4~6行)有所不同上表仅作示例性说明,不是代码的真正执行过程。
------------------------------------------------------------
代码
class Solution {
class Pair {
int height, cnt;
public Pair(int height, int cnt)
{
this.height = height;
this.cnt = cnt;
}
}
public int largestRectangleArea(int[] heights) {
Stack stack = new Stack();
int maxSize = 0, curSize = 0, curCnt = 0;
for (int h: heights)
{
if (stack.empty())
{
stack.push(new Pair(h, 1));
}
else if (h == stack.peek().height)
{
stack.peek().cnt++;
}
else if (h > stack.peek().height)
{
stack.push(new Pair(h, 1));
}
else
{
Pair poped = stack.pop();
curCnt = poped.cnt;
curSize = poped.height*curCnt;
maxSize = curSize>maxSize?curSize:maxSize;
while (!stack.empty() && h < stack.peek().height)
{
poped = stack.pop();
curCnt += poped.cnt;
curSize = poped.height*curCnt;
maxSize = curSize>maxSize?curSize:maxSize;
}
if (stack.empty())
{
stack.push(new Pair(h, curCnt+1));
}
else if (stack.peek().height == h)
{
stack.peek().cnt += curCnt+1;
}
else
{
stack.push(new Pair(h, curCnt+1));
}
}
}
curCnt = 0;
while (!stack.empty())
{
Pair poped = stack.pop();
curCnt += poped.cnt;
curSize = curCnt*poped.height;
maxSize = curSize>maxSize?curSize:maxSize;
}
return maxSize;
}
}