【acwing】单调队列--154. 滑动窗口

单调队列是一种队列(废话)

其中队列的元素保证是单调递增或者是单调递减的

那么队首的元素不就是最小(或最大)的吗?

单调队列两端弹出元素,队尾插入元素

我们结合具体的题目来看看吧:
在这里插入图片描述

解法1:O(n^2)

如果按照常规方法,我们在求a[i] 即i~i+k-1区间内的最值

时,要把区间内的所有数都访问一遍,时间复杂度约为

O(nk)O(nk)O(nk)。有没有一个快一点的算法呢?

解法2:O(n):每个元素最多入队出队一次

很明显,当我们在计算区间 [i−k+1,i][i-k+1,i][i−k+1,i]的最大值时,是不

是区间 [i−k+1,i−1][i-k+1,i-1][i−k+1,i−1]我们之前已经计算过了?那么我们

是不是要保存上一次的结果呢(主要是最大值)?

这时,单调队列登场——

单调队列主要有两个操作:删头和去尾

1.删头

如果队列头的元素离开了我们当前操作的区间,那么这

个元素就没有任何用了,我们就要把它删掉

2.去尾

假设即将进入队列的元素为 XXX,队列尾指针为 tailtailtail,

这时我们要比较二者的大小:

1* X<q[tail]X<q[tail]X<q[tail]

此时q仍然保证着递减性,故直接将 XXX插入队列尾

2* X>=q[tail]X>=q[tail]X>=q[tail]

此时,队列递减性被打破,此时我们做一下操作:

  1. 弹出队尾元素
    因为当前的 q[tail]q[tail]q[tail]不但不是最大值,对于以后的情况
    也不如 XXX更优,所以要弹出
    这好比当前队列尾部的元素是个能力不足的老兵,而新加入
    的新兵更年轻,更能打,当然不要老兵了

  2. 重复执行①,直到满足 X<q[tail]X<q[tail]X<q[tail]或者队列为空为止

  3. 将 XXX插入队列

对于样例而言:

[1 3 -1] -3 5 3 6 7 
q={1},{3},{3,-1} output:3//分别为每次操作的结果
1 [3 -1 -3] 5 3 6 7 
q={3,-1,-3} output:3
1 3 [-1 -3 5] 3 6 7
q={-1,-3},{-1,5},{5} output:5
1 3 -1 [-3 5 3] 6 7
q={5,3} output:5
1 3 -1 -3 [5 3 6] 7 
q={5,6},{6} output:6
1 3 -1 -3 5 [3 6 7]
q={6} output:7
#include<iostream>
using namespace std;

const int N=1000005;
int n,k;
//a[N]存储输入的数组,q[N]存贮符合要求的数组的下标
int a[N],q[N];

int hh=0,tt=-1;

int main(){
    cin>>n>>k;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    
    //求滑动窗口中最小值
    for(int i=0;i<n;i++){
        //判断队头是否还在滑窗之内
        if(hh<=tt && i-k+1>q[hh]){
            hh++;
        }
        while(hh<=tt && a[q[tt]]>=a[i]){//如果队列中的值比当前的值大,那么从队尾弹出(保证是递增的)
            tt--;
        }
        q[++tt]=i;//把当前数的下标存入队列中
        if(i>=k-1){
            cout<<a[q[hh]]<<" ";
        }
    }
    cout<<endl;
    
    
    //求当前窗口中的最大值
    hh=0;//记得初始化...
    tt=-1;
    for(int i=0;i<n;i++){
        //判断队头是否还在滑窗之内
        if(hh<=tt && i-k+1>q[hh]){
            hh++;
        }
        while(hh<=tt && a[q[tt]]<=a[i]){//和求最小值对称就行
            tt--;
        }
        q[++tt]=i;;
        if(i>=k-1){
            cout<<a[q[hh]]<<" ";
        }
    }
    cout<<endl;
}

模板

常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
    while (hh <= tt && check_out(q[hh])) hh ++ ;  // 判断队头是否滑出窗口
    while (hh <= tt && check(q[tt], i)) tt -- ;
    q[ ++ tt] = i;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值