单调栈
用途:O(n)时间,求一个数组的每个元素i,它的左边最近或者右边最近比它小或者大(or大于等于or小于等于)的元素。
- 求最近比它大的,维护一个严格单调递减的栈(栈底到栈顶,是从大到小),while的时候栈顶元素小于等于当前元素,我们pop。(假如是大于等于,那就是单调递减的栈,小于的时候pop)
- 求最近比它小的,维护一个严格单调递增的栈(栈底到栈顶,是从小到大),while的时候栈顶元素大于等于当前元素,我们pop。(小于等于,同上)
求左边那就是从左到右遍历一遍,从右边就是从右到左遍历一遍。
求左右两边第一个比它小的模板
void fun(vector<int> vec){
int n=vec.size();
// left[i] 表示的是在vec数组中 左边最近的比vec[i]小的下标, 假如左边没有最小的存-1
// right[i] 表示的是在vec数组中 右边最近的比vec[i]小的下标, 假如右边没有最小的存n(vec数组长度)
vector<int> left(n),right(n);
//求左右两边最近小的,所以维持从小到大
stack<int> stk;
// 求左边
for(int i=0;i<n;++i){
// 栈不为空 && 栈顶元素大于当前元素
// 也就破坏了单调递增性, 所以需要出栈
// 假设现在 栈里是 4 6, 要入栈的是5, 因为5比6小, 5以后的元素,最近的肯定轮不到6,有5在,所以6需要出栈。
while(stk.size() && vec[stk.top()]>= vec[i])
stk.pop();
if(stk.empty())
left[i]=-1; // 假如左边没有最小的存-1
else
left[i]=stk.top();
stk.push(i);
}
while(stk.size())
stk.pop();
//求右边
for(int i=n-1;i>=0;--i){
while(stk.size() && vec[stk.top()]>=vec[i])
stk.pop();
if(stk.empty())
right[i]=n;
else
right[i]=stk.top();
stk.push(i);
}
do something
}
//另一种实现,我觉得不好 不要看!!
void fun(vector<int> vec){
stack<int> inc; //求左右两边第一个小的,用递增栈,栈中存的是下标
// 遍历数组
for(int i=0;i<vec.size();++i){
//栈非空,且当前遍历到的元素的值 小于等于 栈顶元素代表的值(破坏了递增性)
while(inc.size() && vec[i]<= vec[inc.top()]){
//当前遍历到的元素的值小于栈顶元素代表的值的逻辑
if(vec[i]<vec[inc.top]){
// 弹出栈顶元素index。对这个元素进行处理
//假如栈不空,那么index左边第一个比它对应值小的下标就是栈顶,否则-1(null)
//index右边第一个比它对应值大的下标就是i
int index=inc.top();
inc.pop();
int left= inc.empty()?-1:inc.top();
int right =i;
/*
do something
*/
}
// 当前遍历到的元素的值等于栈顶元素代表的值的逻辑
else {
do something
//可能是插入到栈顶元素中去(这时候stack存的是vector<int>)
}
}
inc.push(i);
}
// 数组虽然遍历完了,但是栈里可能还有元素
/* 这时候对于栈里的每个元素,它们右边第一个比它小的元素都是右边界heights.size()或者说没有(null),而左边第一个比每个元素小的元素,跟上面的逻辑一样,依旧是栈里的下一个元素,or 栈空的话,是左边界(-1 或者说是没有)
*/
int end=heights.size();
while(inc.size()){
int index=inc.top();
inc.pop();
int left= inc.empty()?-1:inc.top();
int right =end;
do something;
}
}
经典例题
leetcode 84. 柱状图中最大的矩形 hard (重要)_speargod的博客-CSDN博客
leetcode 42. 接雨水 hard_speargod的博客-CSDN博客
leetcode 739. 每日温度 medium _speargod的博客-CSDN博客
leetcode 503. 下一个更大元素 II medium _speargod的博客-CSDN博客
单调队列
应用:滑动窗口的最值(最大最小问题)
经典例题: leetcode 239. 滑动窗口最大值 hard(重要)_speargod的博客-CSDN博客
思想: 假设现在求滑动窗口的最大值问题, 我队列现在最后一个元素是6, 现在9要进来,那现在最大值肯定轮不到6了,因为6会比9先出去,并且9比6大。所以我们维护一个单调递减的队列即可。
总结:
不管是单调栈还是单调队列:
求大的,那栈or队列就需要单调递减, 当栈顶or队列尾(小于等于 or 小于)当前元素,那就出栈(队),
求小的,栈or队列就需要单调递增, 当栈顶or队列尾(大于等于or 大于)当前元素,那就出栈(队)
同时栈和队列存的是下标