单调栈、单调队列
2026-08-29 15:50:23
发布于:上海
单调栈、单调队列
单调栈
使用场景:
求每个右边/左边第一个大于/小于其的数字,在内解决这个问题。
核心思想:
及时淘汰"永远不会成为答案的元素"
朴素做法:每个都要向右扫描一次。若且,那么当我们向右寻找时,永远不会先于被选中——任何比大的数学如果在右边,都会先拦住它,那么就应该在出现后淘汰。
实现方法:
维护一个栈,栈内元素从栈底到栈顶递减。
1.新元素到来时,所有比它小的元素,都等来了自己的答案,在弹栈的过程中记录答案。
2.之后,入栈
代码:
int a[N],ans[N],n;\\ans用于存储最终答案
stack<int> st;\\单调栈
for(int i=1;i<=n;i++){
while(!st.empty()&&a[st.top()]<a[i]){ \\持续弹出
ans[st.top()]=i;
st.pop();
}
st.push(i);
}
总结:
| 栈的单调性(栈底到栈顶) | 适用场景 | 触发弹出时的逻辑 |
|---|---|---|
| 单调递减(大->小) | 寻找每个元素左侧/右侧第一个更大的 元素 | 当新元素>栈顶 新元素是栈顶的第一 个更大值。 |
| 单调递减(小->大) | 寻找每个元素左侧/右侧第一个更小的 元素 | 当新元素<栈顶 新元素是栈顶的第一 个更小值。 |
单调队列(双指针)
使用场景:
求每个长为的窗口内的最大值。
同样看浪费的部分:若且,只要在窗口内,就永远不可能是最大值,窗口保留,且可以立刻淘汰。
维护一个双端队列,存下标,对应值从队头到队尾递减:
· 队尾:新元素到来时,把队尾所有不大于它的元素弹掉,再把新元素接在队尾;
· 队头:检查队头下标是否已经滑出窗口,是则弹出;
· 答案:队头即当前最大值。

代码
int n,a[N];
deque<int> q;
//求区间最小值
for(int i=1;i<=n;i++){
while(!q.empty()&&a[q.back()]>=a[i]){
q.pop_back();
}
q.push_back(i);
while(q.front()<=i-k){
q.pop_front();
}
if(i>=k){
cout<<a[q.front()]<<" ";//输出
}
}
//区间最大值
for(int i=1;i<=n;i++){
while(!q.empty()&&a[q.back()]<=a[i]){
q.pop_back();
}
q.push_back(i);
while(q.front()<=i-k){
q.pop_front();
}
if(i>=k){
cout<<a[q.front()]<<" ";//输出
}
}
这里空空如也













有帮助,赞一个