单调栈学习笔记
以洛谷 P5788 【模板】单调栈 为例。
题目描述 - 单调栈的基本作用
题目很简单:给定一个数列,求每一个数往后看第一个比它大的数是谁。
我们来看样例:
51 4 2 3 5显而易见,对于第一个数字 1 ,第一个比它大的数字是 4 ,排在第二位;对于第二个数字 4 ,第一个比它大的数字是 5 ,排在第五位……以此类推。
深入理解单调栈
下面让我们把数据可视化。

首先迈出关键一步:因为题目中要求的是向右边看第一个比当前高的,也就是说已知信息应该在当前的右边。所以我们要以从右向左的顺序来寻找规律。否则,本题将难以解决。
我们先来看最后一个 5 。因为这是我们第一个看的,其右边没有更多信息了,所以按题目输出为 0 。
看完 5 ,往左一个看到 3 。由于 3 比 5 小,所以输出为 5 (这是真实 5 的位置)。同理, 2 的输出为 4 (这是真实 3 的位置)。
现在我们已经看了 2, 3, 5 ,也就是数字 4 的后面有 2, 3, 5。现在看到了数字 4 。我们发现 4 往后看一个是 2 ,然而 ,所以 2 不是我们要找的数,丢掉;丢掉了 2 ,看到 3 ,由于 ,所以 3 也不是要找的数,丢掉;终于看到了 5 ,由于 ,所以 5 就是我们要找的数了。
让我们来思考最后一个数字 1 。显然,它的对应数就是 4 。如果把 1 改成 7 呢?如图:

由于我们已经丢掉了 4 后面所有小于 4 的数字,所以这些被丢掉的数字一定也小于 7 ,我们不用操心这些数字里是否有我们可能要寻找的数字。那么,我们再次进行“丢掉”操作,现在 7 后面的数字有 4, 5 ( 2, 3 已经被 4 丢掉了),而 4, 5 都小于 7 ,所以 4, 5 都被丢掉了,剩下了一个‘空’,所以 7 的答案变成了 0 。
让我们来重新审视一下整个过程:
- 从右往左看,如果后面的数字有比当前的数字小的,就丢掉;
- 丢到剩下面对的第一个比当前的大为止,这个数就是满足要求的。
这样一看,这似乎就是一个栈的操作,而且这个栈中的元素是单调递增或递减的。我们就将其称作单调栈。
来看看 OI-wiki 上对单调栈的定义:
顾名思义,单调栈即满足单调性的栈结构。与单调队列相比,其只在一端进行进出。
代码实现
读入完后,我们需要:
- 从右往左看:
for(int i=n;i>=1;i--)- 弹出栈中所有小于等于当前元素的:
while(!s.empty()&&a[i]>=a[s.top()]){ s.pop();}- 获得当前答案:
ans[i]=(s.empty()?0:s.top());- 将当前元素压入栈中:
s.push(i);注意:输入输出应该使用 scanf ,否则会超时。
Full Code
#include <bits/stdc++.h>using namespace std;const int maxn=3e6+10;int n;int a[maxn];int ans[maxn];stack <int> s;int main(){ cin>>n; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int i=n;i>=1;i--){ while(!s.empty()&&a[i]>=a[s.top()]){ s.pop(); } ans[i]=(s.empty()?0:s.top()); s.push(i); } for(int i=1;i<=n;i++){ printf("%d ",ans[i]); } return 0;}支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!
无穷大?