栈与队列的模拟以及单调栈和单调队列
注:部分模板来自acwing yxc
栈表示的是一种瓶子一样的数据容器,对应STL中的stack,我们只能从瓶子最上面拿东西,但是我们放东西只能从最下面开始放,所以最后放入东西要最先拿出来。虽然说在STL中有相应的容器,但是手动模拟的运行速度会更快一些。
const int N = 1e5 + 10; int tt = 0, hh = 0; int st[N]; int x; //数据进栈 st[++tt] = x; //输出栈顶元素 cout << st[tt] << endl; //删除栈顶元素 tt--; //判断栈是否为空; //由于在创建栈的时候并没有考虑向坐标hh里放元素,所以hh==tt就对应栈空 //如果向hh下放元素,条件则改为 tt<hh; return hh == tt;
tt代表栈顶坐标,hh代表栈底坐标;
队列和栈略有不同,栈是只能从一个方向进出,队列则是从一个方向进入,从另一个方向出,队列对应STL中的queue;
const int N = 1e5 + 10; int tt = -1, hh = 0; int qu[N]; int x; //数据入队 qu[++tt] = x; //输出队头元素 cout << qu[hh] << endl; //删除队头元素 hh++; //判断队是否为空; return hh > tt;
此处我们将tt作为队尾,hh作为队头。
单调栈,例题AcWing 830. 单调栈 - AcWing 代码如下:
# include<bits/stdc++.h> using namespace std; const int N=1e5+10; int a[N]; int tt,hh; int main() { int n; scanf("%d",&n); while(n--) { int x; scanf("%d",&x); while(tt>hh&&a[tt]>=x) tt--; if(hh>=tt) printf("-1 "); else printf("%d ",a[tt]); a[++tt]=x; } return 0; }
只需明确一点,就是在k前面的数如果大于等于k,那么在k之后的数一定不会用到它,就可以删去,这点很容易理解,但比较难想,这就是中间循环
while(tt>hh&&a[tt]>=x) tt--;
的意义,也是整个单调栈的核心,循环的条件我们要保证最后tt>=hh,及栈最后最少剩余0个元素,如果tt<hh,那么栈中就没有元素了,当然这点应根据自己创建的栈来判断;
其次是单调队列,例题:154. 滑动窗口 - AcWing题库 妈的,这题有点难想,可能是我太菜。代码如下:
//队列的创建和数据的输入不再赘述 for(int i=0;i<n;i++) { if(hh<=tt&&i-k+1>q[hh]) hh++; while(hh<=tt&&a[i]<=a[q[tt]]) tt--; q[++tt]=i; if(q[tt]>=k-1) printf("%d ",a[q[hh]]); }
由于最大值和最小值思路相同,不再重复写代码
我们的队列储存的是数组,每次队头的坐标必然是窗口中最小值的坐标,具体分析如下。
每一次我们想要判断,在第 i 个数放入后哪个数最小,既然要判断第 i 个数放入后,那么原来的的队头可能已经出不在窗口内了,所以第一个 if 我们用来判断 q[hh] 所对应的数是否还在窗口内,如果不在了,哪hh++,然后再判断队列里面的数,如果队列里面的数大于等于a[i]的话,那么一定不是最小值,tt--删去(这里从队尾操作了,队列不能从队尾操作,所以建议用数组模拟),一直到遇到比a[i]小的元素,如果不存在,那么就把队列删空,让i进队,a[i]就是窗口中最小的元素。

浙公网安备 33010602011771号