一、栈 先进后出
- 插入元素 stk[ ++ tt] = x
- 弹出元素 tt –
- 判断栈是否为空 tt > 0不空 ,tt < 0空
- 栈顶 stk[ tt ]
二、队列 先进先出 队尾插入元素,队头弹出元素
- 插入元素 q[ ++ tt] = x
- 弹出元素 hh ++ –
- 判断栈是否为空 hh <= tt不空 ,hh > tt空
- 栈顶 q[ hh ]
#include<iostream>
using namespace std;
const int N = 100010;
int stk[N], tt;
//栈
//插入
stk[ ++ tt] = x;
//弹出
tt --;
// 判断栈是否为空
if(tt > 0) not empty
else empty
//队列
//队尾插入元素,队头弹出元素
int q[N], hh, tt = -1;
//插入
q[ ++ tt] = x;
//弹出
hh ++;
//判断队列是否为空
if(hh <= tt) not empty
else empty
//取出队头元素
q[hh]
三、 单调栈(算法时间复杂度O(n))
用的比较多的题型:一列数,找到在它左侧最靠近它且比它小的数,若找到返回该数,找不到返回-1
将这一组数放入栈中,假设我们遍历到了a[y]这个元素之后,如果在a[y]之前存在a[x],a[x] >= a[y],那么我们永远不会选择a[x]了,故a[x]会被a[y]更新掉,即更新掉下降序列。不断更新之后,只剩下单调序列,故为单调栈。
#include<iostream>
using namespace std;
const int N = 100010;
int stk[N], tt;
//栈
//插入
stk[ ++ tt] = x;
//弹出
tt --;
// 判断栈是否为空
if(tt > 0) not empty
else empty
//队列
//队尾插入元素,队头弹出元素
int q[N], hh, tt = -1;
//插入
q[ ++ tt] = x;
//弹出
hh ++;
//判断队列是否为空
if(hh <= tt) not empty
else empty
//取出队头元素
q[hh]
四、 单调队列
常用:求滑动窗口里面的最小(大)值
我们使用一个队列来维护窗口,在这个队列中,靠左的元素如果更大(小),则我们就不可能会将它输出,即被更新掉,更新完后队列中的最小值即为队头。
#include<iostream>
using namespace std;
const int N = 1000010;
int n, k;
int a[N], q[N];
//队列存储的是下标
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++)
{
//判断队头是否已经滑出窗口
//队伍终点是i,起点是i - k + 1,起点的下标已经在队头的右侧,则队列移动一位
if(hh <= tt && i - k + 1 > q[hh]) hh++;
//筛选队列中靠左且比当前元素大的值
while(hh <= tt && a[q[tt]] >= a[i]) tt --;
q[ ++ tt] = i;//先把i插入,i有可能是最小值
//不足k个时,输出队头
if(i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
//最大值
hh = 0, tt = -1;
for(int i = 0; i < n; i++)
{
//判断队头是否已经滑出窗口
//队伍终点是i,起点是i - k + 1,起点的下标已经在队头的右侧,则队列移动一位
if(hh <= tt && i - k + 1 > q[hh]) hh++;
//筛选队列中靠左且比当前元素大的值
while(hh <= tt && a[q[tt]] <= a[i]) tt --;
q[ ++ tt] = i;//先把i插入,i有可能是最小值
//不足k个时,输出队头
if(i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
return 0;
}