单调队列

单调队列

算法思想:

  • 先考虑用普通队列来做该如何做,即用一个队列维护窗口,用入队出队模拟窗口移动O(n),线性扫描队列求最值O(k)

  • 当前队列里右面的数比左面的数小(-1,-3),而且左面的数会在右面的数前面出队列,换句话说,只要-1还在,-3一定在,所以最小值一定不会取到-1,所以-1就可以删掉了。即如果后一位的元素比当前的元素小,当前这个元素就没有意义了,删掉。这样把当前队列里面的冗余元素都删掉之后,剩下的元素是递增的。这样求最小值的时候就可以把O(k)优化到O(1),因为只要取队头就好了。求最大值对称地做就可以了。

  • 总结以上步骤和分析过程:1.用普通队列该怎么做

    ​ 2.将队列中的没有用的元素删掉----->具有了单调性(当前窗口)

​ 3.可以用O(1)时间从队头/队尾取出最值

  • 使用场景:滑动窗口的最值
  • 实现的时候一般手写双端队列(数组模拟)

代码实现:

//例子:滑动窗口
//手写双端队列
//队列里存的是数字在原数组中的下标
int a[N],q[N];
int main()
{
    int n,k;
    cin>>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++)
    {
        //判断当前的队头已经不在当前的窗口中
        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]]);
    }
    cout<<endl;
    //求最大值
    hh=0,tt=-1;
    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]]);
    }
    cout<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/codertea/p/13370950.html