【第十一课】数组模拟栈和队列 / 单调栈 / 单调队列(滑动窗口) (c++代码 / 思路 )(acwing-828,829,830,154)

目录

acwing-828模拟栈

代码如下 

acwing-829模拟队列 

代码如下 

acwing-830单调栈

思路 

代码如下

acwing-154滑动窗口-单调队列

思路 

代码如下


这个数组模拟栈和队列的实现比较简单,我们之前也学过数据结构的内容,比较好实现,就不再多说了 。

acwing-828模拟栈

【第十一课】数组模拟栈和队列 / 单调栈 / 单调队列(滑动窗口) (c++代码 / 思路 )(acwing-828,829,830,154)_第1张图片

模拟栈主要就是遵循先进后出的原则。其他需要注意的都写在注释里了。

代码如下 

#include
using namespace std;
const int N=1e5+10;
int stk[N],tx,m;//tx表示栈顶元素的下标
int main()
{
    cin>>m;
    string order;
    while(m--)
    {
        cin>>order;
        if(order=="push")
        {
            int x;
            cin>>x;
            stk[++tx]=x;//栈内元素下标从1开始,所以前缀++。且tx=0时意味着栈为空
        }
        else if(order=="pop")
        {
            if(tx==0)printf("error\n");//这里多加了一条判断
            tx--;
        }
        else if(order=="empty")
        {
            if(tx==0)printf("YES\n");
            else printf("NO\n");
        }
        else if(order=="query")
        {
            if(tx==0)printf("error\n");//这里多加了一条判断
            printf("%d\n",stk[tx]);
        }
    }
    return 0;
}

acwing-829模拟队列 

【第十一课】数组模拟栈和队列 / 单调栈 / 单调队列(滑动窗口) (c++代码 / 思路 )(acwing-828,829,830,154)_第2张图片

队列遵循先进先出 

代码如下 

#include
using namespace std;
const int N=1e5+10;
int que[N],hh,tt,m;
int main()
{
    hh=0,tt=-1;//hh表示队列头,tt表示队列尾
    cin>>m;
    string order;
    while(m--)
    {
        cin>>order;
        if(order=="push"){
            int x;
            cin>>x;
            que[++tt]=x;
        }
        else if(order=="pop")
        {
            hh++;//删除一个元素直接头指针向后移动一位即可
        }
        else if(order=="empty")
        {
            if(hh<=tt)printf("NO\n");//我们这里模拟的是普通队列,当t<=tt就说明队列里没有元素
            else printf("YES\n");
        }
        else if(order=="query")
        {
            if(hh>tt)printf("error\n");//多加了判断
            else printf("%d\n",que[hh]);
        }
    }
    return 0;
}

acwing-830单调栈

【第十一课】数组模拟栈和队列 / 单调栈 / 单调队列(滑动窗口) (c++代码 / 思路 )(acwing-828,829,830,154)_第3张图片

思路 

这道题要求我们输出数列中每一个数左边离它最近的一个小于它的数。

我们可以把这个数列的元素都放进栈里面,我们要想找这个数左边离他最近的小于它的数,此时我们想一下,当我开始从这个数不断向左找,如果遇到一个大于这个数的数,那么这个数也就永远不会被后续作为答案输出出来,因为我们此时输入的数已经比这个数要小了,后续要在比较也会优先选择我们此时的数,因此我们可以直接将其出栈。那么不断比较大小然后出栈之后,我们所得到的栈里面的元素应该是单调递增的。单调栈由此得来。

代码如下

#include
using namespace std;
const int N=1e5+10;
int stk[N],tt,n;
int main()
{
    cin>>n;
    for(int i=0;i<=n;i++)//因为要找每一个数左边离他最近的小于它的数,所以直接在输入元素的时候就对其进行判断
    {
        int x;
        scanf("%d",&x);
        while(tt && stk[tt]>=x)tt--;//当栈不为空,且栈顶元素大于该数,就将栈顶元素出栈。直到找到第一个<该数的元素
        if(tt)printf("%d ",stk[tt]);//如果在栈不为空时找到了则输出
        else printf("-1 ");//没找到

        stk[++tt]=x;//将这个新的数据入栈
    }
    return 0;
}

另外提一下,我们使用cin cout进行输入输出比较慢,可以通过下面这两行进行提速。

cin.tie(0); 
ios::sync_with_stdio(false);   

【第十一课】数组模拟栈和队列 / 单调栈 / 单调队列(滑动窗口) (c++代码 / 思路 )(acwing-828,829,830,154)_第4张图片

然而对比下来,就算进行了提速,还是没有使用  scanf 和 printf 快。

acwing-154滑动窗口-单调队列

【第十一课】数组模拟栈和队列 / 单调栈 / 单调队列(滑动窗口) (c++代码 / 思路 )(acwing-828,829,830,154)_第5张图片

思路 

题意应该很明白,题干解释的很清楚,就不再多说了。

滑动窗口的特点就是不断向后移动,每移动一次,就会队头出队一个元素,队尾新加入一个元素。 这句话写的很像我们要把输入的元素所在的队列作为队列模型,其实不是hhh。我们输入数据的数组记为a[],这是普通的数组,数组q[]作为模拟单调队列,里面存储的是a数组中可能符合要求的数据的索引下标。(下标从0开始)

这里q数组的工作就是:当窗口向右移动时,快速找到窗口中的最小值或最大值

为保证队列的单调性

当新的元素进入滑动窗口时,代码会从队列的尾部开始,移除所有大于或等于新元素的元素的索引(寻找最小值时),或者移除所有小于或等于新元素的元素的索引(寻找最大值时)。然后将新元素的索引添加到队列的尾部。(这是代码的主要思想,把这里想明白就容易了)

由于队列的单调性,我们可以直接从队头读取最小值或最大值。这使得我们寻找一个滑动窗口内min/max的时间复杂度降为O(1),因为直接输出队头。整段代码时间复杂度为O(n)

代码如下

#include
using namespace std;
const int N=1e6+10;
int n,k;
int a[N],q[N];
int main()
{
    scanf("%d %d",&n,&k);
    for(int i=0;iq[hh])hh++;//i-k+1是当前窗口第一个元素的下标
        //将队列>=当前元素的数据都出队
        while(hh<=tt && a[q[tt]]>=a[i])tt--;
        //将新元素入队
        q[++tt]=i;
        //如果窗口中有至少k个元素,就打印最小值
        if(i>=k-1)printf("%d ",a[q[hh]]);
    }
    puts("");//换行
    hh=0,tt=-1;//清空队列
    //找最大值
    for(int i=0;iq[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;
}

好啦,先写到这里。

如果有问题欢迎指出,非常感谢!!

也欢迎交流和建议哦!

你可能感兴趣的:(算法基础,算法,c++,数据结构)