栈:先进后出
队列:先进先出
一.数列实现栈的各种功能
1.插入 2.弹出 3.判断栈是否为空 4.取栈顶元素
代码实现
int stk[N],tt; //分别代表栈和栈顶的下标
//插入
stk[ ++ tt] = x;
//弹出
tt--;
//判断栈是否为空
if(tt > 0) not empty
else empty
//栈顶
stk[tt];
二.数列实现队列的各种功能
1.插入 2.弹出 3.判断队列是否为空 4.取队头元素或队尾元素
//队尾插入元素,在队头弹出元素
int q[N], hh, tt = -1;
//插入
q[++ tt] = x;
//弹出
hh ++;
//判断队列是否为空
if(hh <= tt) not empty
else empty
//取出对头元素
q[hh];
q[tt]; //队尾
三.单调栈
单调栈(monotonous stack)是指栈的内部从栈底到栈顶满足单调性的栈结构
单调栈应用题型十分有限,最常见的题型 :求每一个数左边离他最近且小于他的数(or最大的数)
例题:
第一种做法暴力遍历代码实现:两重循环
优化代码实现:
每个元素最多只会进栈一次,出栈一次,时间复杂度O(n)
#include<iostream>
using namespace std;
const int N = 100010;
int n;
int stk[N], tt;
int main()
{
cin>>n;
for(int i=0;i<n;i++){
int m;
cin>>m;
while(tt && m <= stk[tt]) tt--;
if(tt) cout << stk[tt]<<' ';
else cout<< -1<<' ';
stk[++tt] = m;
}
return 0;
}
思路分析:
一句话概括,就是如果前面的数 比 后面的数 大或相等,那就永远不会用上,从栈内删除即可
详细说明:
第i个数 左边的数可以用栈存起来,如果前面的数大于后面的数,就把前面的数删掉,最后栈里的数变成了严格单调递增
eg:若stk[3]>=stk[5],就把stk[3]删去 ,因为如果stk[3]是我们的目标值,那么换成stk[5]会更好
倘若遍历到第i个数 , i之前的栈内元素已满足严格的单调上升 , 第i个数的值为m,如果stk[tt]>=m , stk[tt]永远都不会被当成答案 , 那么就删掉stk[tt] , tt-- , 直到出现一个比m小的数,最后把m插入到栈顶
应用:每次输入一个数x到栈,如果stk[tt]>=x, 那就把栈顶删掉,当出现stk[tt]<x,那这就是第一个出现的比x小的数,输出stk[tt]
运行时间
建议输出输入使用scanf 和 printf,快了将近10倍 ,也可以加上cin.tie();
四.单调队列
经典例题:
代码实现:
#include<iostream>
using namespace std;
const int N = 100010;
int n,k;
int a[N],q[N]; //a数组记录数值,q数组记录下标
int main(){
scanf("%d%d",&n, &k);
for(int i=0;i < n; i++) scanf("%d", &a[i]);
int hh = 0, tt = -1;
for(int i = 0; i < n; i ++)
{
//判断队列是否已经滑出窗口
if(hh <= tt && i - k + 1 > q[hh]) hh ++;
//如果即将入列的值a[i] <= 队列尾部a[q[tt]] , 那么将其移出队列
while (hh <= tt && a[q[tt]] >= a[i]) tt --;
//q存入新的索引i ,即将a[i]插入队列
q[++ tt] = i;
if(i >= k - 1) printf("%d ",a[q[hh]]);
}
puts("");
hh = 0, tt = -1;
for(int i = 0; i < n; i ++)
{
//判断队列是否已经滑出窗口
if(hh <= tt && i - k + 1 > q[hh]) hh ++;
while (hh <= tt && a[q[tt]] <= a[i]) tt --;
q[++ tt] = i;
if(i >= k - 1) printf("%d ",a[q[hh]]);
}
puts("");
return 0;
}
总结
单调栈和单调队列的代码实现,做题步骤都十分相似
如何维护单调性:新元素入栈/队列时,会与栈顶元素/队尾元素进行比较,使得栈/队列始终保持单调性
单调栈和单调队列做题步骤:
1.先考虑用栈和队列暴力模拟这个问题
2.再想想在栈和队列中哪些元素是没有用的,把所有没有用的元素删掉
3.观察剩下的元素是否有单调性,有的话就可以做优化
例如:
取极值 ->可以直接取两个端点
查找一个值->可以用二分