用数组模拟栈和队列(AcWing830 AcWing 154)

AcWing 830. 单调栈

 思路是让这题变成一个单调递增的数列,若新出现的一个数比前面的数小,那么就逐个覆盖。每次输出栈尾就是左边不大于他的第一个数。

#include<iostream>
using namespace std;
const int N =100010;
int tt=0;
int stk[N];
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        int x;
        cin>>x;
      while(tt&&stk[tt]>=x)tt--;
      if(tt)cout<<stk[tt]<<' ';
      else cout<<-1<<' ';
      stk[++tt]=x;              
    }
    return 0;
}

                                AcWing .滑动窗口

  

这道题目的时间限制卡得比较紧,需要用 O(n)O(n) 时间复杂度的算法来做。

这是一道单调队列的模板题,以求最小值为例:

我们从左到右扫描整个序列,用一个队列来维护最近 kk 个元素;
如果用暴力来做,就是每次都遍历一遍队列中的所有元素,找出最小值即可,但这样时间复杂度就变成 O(nk)O(nk) 了;
然后我们可以发现一个性质:
如果队列中存在两个元素,满足 a[i] >= a[j] 且 i < j,那么无论在什么时候我们都不会取 a[i] 作为最小值了,所以可以直接将 a[i] 删掉;
此时队列中剩下的元素严格单调递增,所以队头就是整个队列中的最小值,可以用 O(1)O(1) 的时间找到;
为了维护队列的这个性质,我们在往队尾插入元素之前,先将队尾大于等于当前数的元素全部弹出即可;
这样所有数均只进队一次,出队一次,所以时间复杂度是 O(n)O(n) 的。

#include<iostream>
using namespace std;
int n,k;
const int N =1000010;
int a[N],q[N]; //q数组存储的是下标。
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    scanf("%d",&a[i]);
    int hh=0,tt=-1; //hh表示队首,tt表示队尾。
    for(int i=0;i<n;i++)
    {
        if(hh<=tt&&q[hh]<i-k+1)hh++; //如果满足题意的数列此时超过k个,需要让hh向前加1。
        while(hh<=tt&&a[q[tt]]>=a[i])tt--;//如果队尾元素比新出现的元素小,那么他一定不是那个最小的值
        q[++tt]=i;//存储下标
        if(i>=k-1) printf("%d ",a[q[hh]]);//输出最小值
    }
     hh=0,tt=-1;
     cout<<endl;
    for(int i=0;i<n;i++)
    {
        if(hh<=tt&&q[hh]<i-k+1)hh++;
        while(hh<=tt&&a[q[tt]]<=a[i])tt--; //最大值和最小值思路是一样的,将这里的大于号改成小于号,那么就是输出最大值。
        q[++tt]=i;
        if(i>=k-1) printf("%d ",a[q[hh]]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zyz010206/p/12370231.html
154