栈与队列的模拟以及单调栈和单调队列

注:部分模板来自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]就是窗口中最小的元素。

 

posted @ 2021-09-28 10:02  一心如旧  阅读(62)  评论(0)    收藏  举报