84. 柱状图中最大的矩形
思路:方法一:我们可以两层循环枚举左右边界,通过一直更新最小高度值,找到最优值,但时间复杂度为0(10^10).
方法二:一层循环枚举每个点,再加上一个循环寻找左右边界,更新最优值,时间复杂度为0(10^2)
方法三:我们根据方法二,来进行优化。我们可以维持一个单调递增的栈,然后每次找到比他小的值的下标,时间复杂度为0(10^5),具体细节看代码。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> l,r;//左右两边的递增栈
vector<int> v1(heights.size()),v2(heights.size());//保存每个下标i对应的左右两个边界(这两个边界对应的高度都是首次低于heights[i])
for(int i=0;i<heights.size();i++){//先从左到右遍历,找到左边最近的下标
while(!l.empty()&&heights[l.top()]>=heights[i]){//栈不为空且栈顶元素对应的高度>=当前的高度
l.pop();//进行出栈操作
}
if(!l.empty())v1[i]=l.top();//如果栈不为空,说明目前栈顶元素对应的高度是首个低于当前高度的。
else v1[i]=-1;//栈顶为空,说明没有比他还低的高度,那么我们默认为-1,便于后续计算面积。
l.push(i);//插入当前高度,说明每个数都只会进栈一次
}
for(int i=heights.size()-1;i>=0;i--){//从右到左遍历,找到右边最近的下标
while(!r.empty()&&heights[r.top()]>=heights[i]){//栈不为空且栈顶元素对应的高度>=当前的高度
r.pop();//进行出栈操作
}
if(!r.empty()) v2[i]=r.top();//如果栈不为空,说明目前栈顶元素对应的高度是首个低于当前高度的。
else v2[i]=heights.size();//栈顶为空,说明没有比他还低的高度,那么我们默认为heights.size(),便于后续计算面积。
r.push(i);//插入当前高度,说明每个数都只会进栈一次
}
int maxx=0;
for(int i=0;i<heights.size();i++){//遍历每个点,找到最优值
maxx=max(maxx,(v2[i]-v1[i]-1)*heights[i]);//-1是因为保存的下标都是不满足要求的
printf("%d-%d\n",v1[i],v2[i]);
}
return maxx;
}
};
思路:方法四,是对方法三时间和空间的一个优化。我们会发现每次出栈操作都是栈顶的高度大于当前的高度,那么对于栈顶元素来说,右边第一个比他低的下标,就是当前的i。那么我们在第一次循环当中就可以保存到左右两边的边界。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> l;
vector<int> v1(heights.size()),v2(heights.size());
for(int i=0;i<heights.size();i++){
while(!l.empty()&&heights[l.top()]>=heights[i]){
v2[l.top()]=i;//对于栈顶元素来说,右边第一个比他低的下标,就是当前的i
l.pop();//一定要明确,每个点都会进栈一次
}
if(!l.empty())v1[i]=l.top();
else v1[i]=-1;
l.push(i);
}
while(!l.empty()){//如果栈不为空,说明,右边的高度都是比他大,符合要求的。
v2[l.top()]=heights.size();
l.pop();
}
int maxx=0;
for(int i=0;i<heights.size();i++){
maxx=max(maxx,(v2[i]-v1[i]-1)*heights[i]);
printf("%d-%d\n",v1[i],v2[i]);
}
return maxx;
}
};