最近稍微补了一下单调栈的习题(虽然以前做过),加深一下理解。
力扣84 :柱状图中最大的矩形
思路:
首先考虑一下暴力的做法,枚举每根柱子,它能延申的面积 = 左右两边第一个小于他的数组成的宽 * 这个数的值。
就类似与中间这个黄色的柱子,大于它的都可以延申,直到小于它的就不行,所以这个题目就转化成了枚举每个柱子,并且求每个柱子左右两边第一个小于它的数的位置。
我们可以类似用双指针的写法写一个O(n^2)的做法
代码:
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int ans = 0;
vector<int>a;
a.push_back(0);
int n = heights.size();
for(int i = 0; i < heights.size(); i++)
a.push_back(heights[i]);
for(int i = 1; i <= n; i++){
int l = i;
while(l - 1 >= 1 && a[l - 1] >= a[i])l--;
int r = i;
while(r + 1 <= n && a[r + 1] >= a[i])r++;
ans = max(ans,a[i] * (r - l + 1));
//cout<<a[i] * (r - l + 1)<<endl;
}
return ans;
}
};
这个会超时那么我们考虑一下怎么优化?
单调栈可以用来求左边/右边第一个大于/小于它的数,并且有一个很好的复杂度
O
(
n
)
O(n)
O(n),这是单调栈的应用场景,我们考虑怎么把这个场景带到我们这个题中,我们可以构造一个单调递增的栈,栈里面存的是这个元素对应下标,当
a
[
s
t
a
c
k
[
t
o
p
]
]
<
a
[
i
]
a[stack[top]] < a[i]
a[stack[top]]<a[i],说明
s
t
a
c
k
[
t
o
p
]
stack[top]
stack[top]右边第一个小于它的数找到了,并且由于栈是单调递增的,所以左边第一个小于它的数就是
s
t
a
c
k
[
t
o
p
−
1
]
stack[top - 1]
stack[top−1],所以我们求一个长度更新答案即可。最后可能栈里面还有元素需要再处理一下。
核心代码:
ans = max(ans,abs(stack[top] - stack[top - 1] + abs(i - stack[top]) - 1) * a[stack[top]]);
代码:
class Solution {
public:
int h[100000];
int largestRectangleArea(vector<int>& heights) {
vector<int>a;a.push_back(-1);
int n = heights.size();
for(int i = 0; i < n; i++){
a.push_back(heights[i]);
}
int top = 0;
h[++top] = 0;
int ans = 0;
for(int i = 1; i <= n; i++){
while(top != 1&& a[i] < a[h[top]]){
ans = max(ans,abs(h[top] - h[top - 1] + abs(i - h[top]) - 1) * a[h[top]]);
top--;
}
h[++top] = i;
}
while(top != 1){
ans = max(ans,(abs(h[top] - h[top - 1]) + n - h[top]) * a[h[top]]);top--;
}
return ans;
}
};
总结:个人感觉还是比较难想的,代码实现也有一定的难度。但是单调栈的应用场景是很有限的只有是求左边两边第一个大于/小于当前这个数的位置的题,我们可以考虑单调栈。