方法一(暴力解法):
基本思路
计算矩形的面积无非就是底乘以高,但是固定底来依次计算高是不容易的,因为可以用来的底部的面积从左到右有很多中可能,所以可以尝试固定给出的几种高度值,来计算可行的底部信息。
实现代码
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int max = 0;
for (int i = 0;i < heights.size();i++) {
int begin=i,end=i;
for (int j = i;j >= 0;j--) {
if (heights[j] < heights[i])
{
break;
}
else {
begin = j;
}
}
for (int k = i;k < heights.size();k++) {
if (heights[k] < heights[i])
{
break;
}
else {
end = k;
}
}
if (heights[i] * (end - begin + 1) > max)
max = heights[i] * (end - begin + 1);
}
return max;
}
};
int main() {
vector<int> v;
v.push_back(2);
Solution s;
cout << s.largestRectangleArea(v);
return 0;
}
提交结果及分析
虽然利用暴力解法可以得到正确结果,但是会出现超时的问题
方法二:
实现思路
数据结构:栈
- 栈里面维护的是高度数组的下标
- 栈是单调递增栈,也就是维护数据高度是依次递增的
具体的过程
- 首先在栈中压入-1
- 遍历高度数组,只要比当前高度大就向栈中压入下标,否则弹栈,直到栈顶小于当前元素或者栈为空,然后再将元素压入栈中
- 在弹栈的同时也要计算面积,同时适当的更新最大面积
- 都遍历完之后栈顶元素不为空,再接着计算面积,同时适当的更新面积
原理及举例说明
栈 | i | max |
---|---|---|
-1,0 | 1 | 0 |
i为当前遍历的下标值,max为当前最大的面积值,栈为存储的下标值
如图所示,当前i值为1,heights[1]<heights[0],这种情况下弹栈,类似于木桶效应,所以0的面积可以确定,面积的高为height[0],面积的底为(右侧的下标值-左侧下标值-1)注意这里面左侧指的是栈顶元素,右侧为当前i值
计算得到面积为2*(1-(-1)-1)=2*1=2
更新max值,并将i=1压入栈中
栈 | i | max |
---|---|---|
-1,1 | 2 | 2 |
这种情况下直接将i值压入栈中,现在考虑i为4情况,进行处理
栈 | i | max |
---|---|---|
-1,1 ,2,3 | 4 | 2 |
此时heights[3]的面积可以确定,为绿色区域的面积
先弹栈,然后计算面积
栈 | i | max |
---|---|---|
-1,1 ,2 | 4 | 2 |
面积的底为(i值 - 栈顶的下标值-1)
面积=heights[3]*(4-2-1)=6
由于此时栈顶下标为2,对应的高度值heights[2]>heights[4]
接着弹栈,然后计算面积
栈 | i | max |
---|---|---|
-1,1 | 4 | 6 |
面积的底为(i值 - 栈顶的下标值-1)
面积=heights[2]*(4-1-1)=10
栈 | i | max |
---|---|---|
-1,1 | 4 | 10 |
此时栈顶为1,对应的heights[1]<heights[4],所以可以将4压入
栈 | i | max |
---|---|---|
-1,1 ,4 | 4 | 10 |
依照此规则,最后再将5压入
栈 | i | max |
---|---|---|
-1,1 ,4,5 | 4 | 10 |
5是数组中最后一个元素,很明显现在栈并不为空,所以要依次弹栈计算面积
之前面积的底为(i值 - 栈顶的下标值-1),现在已经是最后一个元素了,所以可以另i值为数组的大小n=6,计算出面积同时更新max值即可(注意只有比max值大的时候才更新)
实现代码
#include <iostream>
#include <stack>
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int amax = 0;
stack<int> s;
s.push(-1);
for (int i = 0;i < heights.size();i++) {
while (s.top() != -1 && heights[s.top()] >= heights[i]) {
int cur = s.top();s.pop();
int area = (i - s.top() - 1)*heights[cur];
amax = max(area, amax);
}
s.push(i);
}
while (s.top() != -1) {
int cur = s.top();s.pop();
int area = (heights.size() - s.top() - 1) * heights[cur];
amax = max(area, amax);
}
return amax;
}
};
int main() {
vector<int> v;
v.push_back(2);
v.push_back(1);
v.push_back(5);
v.push_back(2);
v.push_back(5);
v.push_back(8);
v.push_back(2);
v.push_back(1);
Solution s;
cout << s.largestRectangleArea(v);
return 0;
}
提交结果及分析
时间复杂度和空间复杂度均为O(n)
知识补充
相似题目
两种方法的分析
方法一的优点在于比较简单,容易想到,但缺点也是显而易见的时间复杂度偏高。
方法二是用栈这一数据结构,用空间换时间,时间复杂度为O(n)
最后的总结
我自己最开始是没有想通这道题为什么分类在栈这个标签中,看了别人的题解才知道,原来是利用了栈后进先出的计算,也就是和后进来的面积会较早的算出。所以要理解数据结构的特点,明白栈的这种是数据结构具有后进先出的特性,这样才能合理应用在解题中。
另一点就是要明白时间和空间复杂度很大程度上都是能够相互转换的,可以用空间去换时间,也可以用时间去换空间,但尽量追求二者的复杂度都不要太高。