单调栈:栈中加入顺序性,元素是严格单调递增或递减的。
但是说清楚方向: 从栈底到栈顶的递增栈,还是从栈顶到栈底的递增栈
具体操作:(以底到顶递减为例)
1.如果新的元素比栈顶元素小,入栈
2.如果新的元素比栈顶元素大,就一直把栈内元素弹出来,直到栈顶比新元素大,新元素入栈

应用:多用于求解元素的左右大小边界问题

eg 1 5 4 2 3 7 9 那么我要找到每个数向左第一个比自身大的数 O(n^2)
用单调栈来优化:
元素间大小判断:对于当前遍历到的数,栈顶为答案。
1.向左找第一个比自身大的数。 左->右遍历数组 模型:底到顶的递减栈---怎么记:找大的,就保证栈底最大
首先明确,栈满足后进先出,所以栈顶元素一定就是离当前元素最近的元素

可以得出的结论是:(1)如果ai入栈时,栈为空,说明ai左边没有比它大的数
(2) 用递减栈来保存已经遍历过的数,当枚举到第i个数时,栈中的数就是可能比第i个数大的数
这个和栈顶比较。栈顶比i大 栈顶就是答案,否则就连续出栈,知道一个栈顶比i大,
此时记录答案,再把i入栈
Q:栈用来保存已经遍历过的数,但是栈遍历过程中是会执行出栈操作的,意味着 栈中并没有保存所有的遍历
过的数,不会漏掉答案吗?
不会 因为出栈的数x1,x2都是比当时新的栈顶y小的数,那么现在遍历到i
假设x1,x2,y>1 由于y离i更近,所以x1,x2一定不会是答案,所以即便是x1 x2没在栈中也无妨
记录每个入栈元素的答案
1.向左找第一个比自身大的数。 左->右遍历数组 模型:底到顶的递减栈---怎么记:找大的,就保证栈底最大
2.向左找第一个比自身小的数。 左->右遍历数组 模型:底到顶的递增栈---怎么记:找小的,就保证栈底最小
3.向右找第一个比自身大的数。 右->左遍历数组 模型:底到顶的递减栈---怎么记:找大的,就保证栈底最大
别找错方向了!!
4.向左找第一个比自身大的数。 右->左遍历数组 模型:底到顶的递增栈---怎么记:找小的,就保证栈底最小
代码编写

写法二:记录每个出栈元素(栈顶)的答案
1.向左找第一个比自身大的数。 右->左遍历数组 模型:底到顶的递减栈---怎么记:找大的,就保证栈底最大
因为在栈里面的数据在右边,而要入栈的元素才在左边
(1)当栈为空时,直接入栈
(2)当遍历到ai时,如果ai>栈顶, 就要连续出栈, 此时ai就是连续出栈的数的答案
栈顶出栈时记录栈顶的的答案 否则ai<=栈顶 , ai直接入栈
(3)当遍历完后,若此时栈非空,战中的数据就没有答案, 连续出栈 把答案记录为-1
例题一:

时间复杂度为O(n),每个元素入栈和出栈各一次
代码中的关键点:
while循环仅弹出比当前元素arr[i]大或等于的栈顶元素。- 每个元素被弹出后,不会再次入栈(因为后续元素无法满足
arr[k] >= arr[j]的条件)。 - 因此,所有元素的总弹出次数等于入栈次数,均为
n次。
cpp
#include<bits/stdc++.h>
using namespace std;
int arr[3000005];
int ans[3000005];
int f(int i)
{
return ans[i];
}
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>arr[i];
}
stack<int>s;
for(int i=n;i>=1;i--) //从右往左遍历
{
while(!s.empty()&&arr[i]>=arr[s.top()]) //等于都不行,这里加入等号时会进入循环,把栈顶元素踢掉
{
s.pop();
}
//循环退出,判断一下
if(s.empty())
{
ans[i]=0;
s.push(i);
}
else
{
ans[i]=s.top();
s.push(i);
}
}
//cout<<"\n下标为:";
for(int i=1;i<=n;i++)
{
cout<<f(i)<<" ";
}
}
例题2https://www.luogu.com.cn/problem/P15150#ide

cpp
void left_stack() //左找最大
{
stack<int> st; //构造栈底大,栈顶小
for(int i=1;i<=n;i++)
{
while(!st.empty()&&H[st.top()]<=H[i]) // 不为空并且栈顶比较小
{
st.pop();
}
if(st.empty())
{
dis[i]=INT_MAX;
st.push(i);
}
else
{
dis[i]=abs(i-st.top());
st.push(i);
}
}
}
void right_stack() //右找最大
{
stack<int> st; //构造栈底大,栈顶小
for(int i=n;i>=1;i--)
{
while(!st.empty()&&H[st.top()]<=H[i]) // 不为空并且栈顶比较小
{
st.pop();
}
if(st.empty())
{
// dis[i]=min(dis[i],); 可以不管
st.push(i);
}
else
{
dis[i]=min(dis[i],abs(i-st.top()));
st.push(i);
}
}
}