单调栈与单调队列
1. 单调栈
单调栈,顾名思义就是一个具有单调性的数列,而且它具有栈的性质。例如这题:830. 单调栈 - AcWing题库
实现单调栈只需要一个for循环即可,这里是单调递增的单调栈的代码:
for (int i = 0; i < n; i++)
{
int a;
cin >> a;
while (tt && b[tt] >= a)--tt;
if (tt)cout << b[tt] << ' ';
else cout << -1 << ' ';
b[++tt] = a;
}
下边来讲讲原理:
- 首先是利用while循环将栈顶的元素与a对比。这里我们要得到左边第一个小的数,如果栈顶元素大于a,就将栈顶元素去掉。
- 然后判断栈是否为空,如果栈不为空,就输出栈顶元素,否则输出-1。
- 最后将a加入到栈顶。
2.单调队列
单调队列,顾名思义就是一个具有单调性的数列,而且它具有队列的性质。例如这题:154. 滑动窗口 - AcWing题库
实现单调序列的只用一个for循环,这里分别为单调递增和单调递减:
for (int i = 0; i < n; i++)
{
if (h <= t && q[h] <= i - k)h++;//判断读取的元素的数量
while (h <= t && a[q[t]] >= a[i])t--;//删除冗余的数
q[++t] = i;
if (i >= k - 1)printf("%d ", a[q[h]]);
}
puts("");
h = 0, t = -1;
for (int i = 0; i < n; i++)
{
if (h <= t && q[h] <= i - k)h++;
while (h <= t && a[q[t]] <= a[i])t--;
q[++t] = i;
if (i >= k - 1)printf("%d ", a[q[h]]);
}
puts("");
下边来讲讲原理,就以第一个求最小值的单调递增队列为例:
-
首先是判断队列自上次将对头元素输出后读取多少元素(注意不是储存了多少元素),如果达到了k个,就将对头后移一位。队列只要保证每次读取的k个元素单调递增即可。
-
然后是利用一个while循环比较队列中的元素,这里我们要得到最小值,就将队列中冗余的数(即比队尾数a[i]大的数)删除,如此重复是最小值到达对头。
-
最后在每次窗口移位时将对头元素输出即可。
求最大值的单调递减队列同理,将冗余的数的判断改为小于队尾的数即可。