84. Largest Rectangle in Histogram
介绍
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.
For example,
Given heights = [2,1,5,6,2,3]
,
return 10
.
题意,寻找柱型图中最大的矩阵面积值。
解答
这是LeetCode上的一道经典题目,难度也比较大。自己思考了半天也没找到合适的解法,参考下网上的解法,做个记录。
方法1
暴力破解:
这个方法应该每个人都能够想到,但是估计也是一眼就能排除的答案。
穷举任意两条边,然后找到这两条边中的最小高度,组成矩形,然后计算矩形面积,取最大值就行了。
任意两条边的确定就是任意两个子数组,时间复杂度为O(n^2),查找两条边之间的最小高度是O(n)。时间复杂度为O(n^3)。
方法二:
我们直到,对于得到的最大的矩形面积,它肯定是以某个下标上的高度为高度,然后往两边扩展一定宽度得到的。
所以,我们只需要以数组中的每一个下标上的高度为基准,求出相应的矩形面积。计算得到最大值就可以了。
时间复杂度为O(n^2)。
方法三
这个方法才是本篇文章的重点。
在看这个方法的时候,我找了好几篇文章才看懂。感觉算法这个东西就是就算自己懂了,也很难和不懂的人讲清楚,自己不懂,只听别人讲也很难明白。所以推荐大家还是多按照算法步骤自己一步一步来计算一下,就很容易就能理解了。
这个解法的核心在于用栈来保存一个高度依次递增的序列。
对于这样一个高度序列[1,2,3,4,5]
。如果我们想要计算矩阵面积的最大值。因该是1*5 vs 2*4 vs 3*3 vs 4*2 vs 5*1
。这几个值进行比较。
但是给定的序列,显然并不全都是有序增长的。如对于示例序列,[2,1,5,6,2,3]
就不是有序增长的。
那么关键就是我们遇到了一个逆序序列,应该怎样处理这个逆序序列呢?
对于一个栈,如果栈为空,或者当前下标的高度大于栈顶的值,我们就将这个高度值压入栈中。
如果当前下标的高度小于栈顶的值,我们就将弹出栈顶元素的值,然后以栈顶元素的值为高度,连续弹出的次数为宽度,计算相应的矩形面积。然后我们将弹出的高度的值都换成这个当前下标的值,并且当前下标的值自身也需要压入。
下面是操作的模拟过程:
res为最大的矩阵大小,初始值为0,。
- 下标为0时,遇到高度值为2,此时栈为空,直接压入栈。
- 下标为1时,遇到高度值为1,此时小于栈顶元素,弹出2,计算相应的矩阵面积为 2*1。res = 2。然后将栈中弹出的元素的值换为1压入。再压入1。此时栈内值为
1,1
。 - 对于下标2,3,都大于栈顶元素,直接压入。此时栈内值为
1,1,5,6
。 - 对于下标值为4,元素值为2,需要连读弹出6和5,弹出过程计算相应的矩形面积
6*1
,和5*2
,得到res = 10。然后将元素换为2,压入。此时栈内值为1,1,2,2,2
。 - 对于下标5,压入高度3。此时栈内值为
1,1,2,2,2,3
。 - 最后遍历结束后,栈中就是一个增序数列,然后计算它的矩形面积。然后不断的旧的res比较,最终得到结果值。
我们来说下,为什么这个算法能够得到正确值。
其实很简单,对于栈,我们遍历结束之后,他内就存储着当前这个结点到达尾部时候,矩形的高度的值。
如最后栈内值为1,1,2,2,2,3
。就代表了最后一个单位宽度的矩形的高度是3,最后两个单位宽度的矩形的高度是2。
可以参考下图形看看是不是这个样子。
那么除此之外,如果矩形的最后一条边不是尾部呢?
还记的我们遇到逆序时候的处理吗?如果遇到了逆序,我们需要将栈中元素不断弹出,然后计算矩形面积,直到遇到小于当前高度的值,并将弹出的值都换为当前高度重新压入。
所以在这个时候,相应矩阵的值,我们已经计算过了。
因为我们得到的结果就一定是最终的结果。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> help;
int res = 0;
for(int i = 0; i < heights.size(); ++i)
{
int count = 0;
while(!help.empty() && help.top() > heights[i])
{
int h = help.top();
help.pop();
++count;
res = max( res,h*count);
}
while(count-- > 0)
help.push(heights[i]);
help.push(heights[i]);
}
int count = 0;
while(!help.empty())
{
int h = help.top();
help.pop();
++count;
res = max( res,h*count);
}
return res;
}
};
我们可以在高度序列中添加一个最小值,作为哨兵,它将代替上述代码中最后检查元素是否为空,并计算矩阵面积的操作。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> help;
heights.push_back(-1);
int res = 0;
for(int i = 0; i < heights.size(); ++i)
{
int count = 0;
while(!help.empty() && help.top() > heights[i])
{
int h = help.top();
help.pop();
++count;
res = max( res,h*count);
}
while(count-- > 0)
help.push(heights[i]);
help.push(heights[i]);
}
heights.pop_back();
return res;
}
};