Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5Mobile wallpaper 6
841 字
4 分钟
单调栈&单调队列

单调栈#

基本概念#

单调栈,顾名思义,这个栈是单调递增/递减的。

  • 递增栈:越靠近栈顶的元素越大,如push顺序为 11 22 33 44 55
  • 递减栈:越靠近栈底的元素越大,如push顺序为 55 44 33 22 11

问题引入#

P5788 【模板】单调栈

用途#

使用栈,维护一个单调的序列,通过栈顶的最值来进行计算或转移。

操作#

对于每个元素,将其和栈顶->栈底的元素一次比较,不符合的元素出栈,栈顶为当前维护的栈的最值。

例如我们要维护一个降序序列,有push顺序为 88 44 66 11 99 22 44 00

那么第i步操作后栈的状态为:88;88 44;88 66;88 66 11;99;99 22;99 44;99 44 00

int top = 0; // 栈顶指针初值
for (int i = 1; i <= n; i++) {
while (top > 0 and a[q[top]] < a[i]) {
cout << a[q[top]] << endl; // 输出栈顶值
top--; // 栈顶出栈
}
q[++top] = i; // 栈顶入栈
}

在单调栈入栈过程中,入栈的是元素的下标,而不是元素的值。但下文为了直观,记录的是元素值,实际写题时切记要记录下标

对于单调栈而言,记录下标不仅能获知当前元素值,还能方便获取其前后元素位置信息,这在需要回溯或计算区间问题时非常重要。此外,入栈下标后,可追踪元素更新,并在栈顶位置快速找到需要的元素,从而保持栈中数据的单调性并简化维护过程。另外,引入下标可以处理重复出现的元素,用下标作为能唯一标识一条记录的属性组。

考虑如下数组构造单调栈a=[1,1,4,5,1,4]a=[1,1,4,5,1,4],构造递增栈只记录元素的话是[1,1,1,4][1,1,1,4],这个如果题目所求为输出第一个比当前元素小的元素,那可以找到,但是如果是第一个比当前元素大的元素的下标,那这时无法确定具体是第几个11比当前元素小。所以构造单调栈所记录的属性必须是下标,而不是元素值

另外,在构造单调栈时,循环条件为while (top > 0 and a[q[top]] < a[i]),而不是while (top > 0 and a[q[top]] <= a[i])循环条件是严格小于(或等于),相同元素必须全部记录,而不是当前元素小于等于栈顶元素就弹出栈顶元素

考虑如下数组构造单调栈a=[1,2,2,2,2,2]a=[1,2,2,2,2,2],正确构造递增栈为a=[1,2,2,2,2,2]a=[1,2,2,2,2,2],可以找出第一个大于数组下标 11 的元素下标为 22(数组从1开始);但是如果错误记录,元素和栈顶相等的话,那构造出来时a=[1,2]a=[1,2],这个 22 是最后一个22,下标为 66 ,但事实上第一个大于下标 11 的元素下标为 22 ,是第一个 22 ,而不是第五个 22

void solve() {
int n;
cin >> n;
vector<int> a(n + 1), q(n + 1), ans(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int top = 0;
for (int i = 1; i <= n; i++) {
while (top > 0 and a[q[top]] < a[i]) {
ans[q[top]] = i;
top--;
}
q[++top] = i;
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << " \n"[i == n];
}
}
单调栈&单调队列
https://mizuki.mysqil.com/posts/单调栈-单调队列/
作者
LANSGANBS
发布于
2025-01-04
许可协议
CC BY-NC-SA 4.0
封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00