单调队列 -- 滑动窗口

滑动窗口

求每个滑动窗口的最小值、最大值

从左到右扫描整个序列,用一个队列来维护最近 k 个元素,每k个元素就是一个窗口;
暴力枚举就是每次都遍历一遍窗口中所有元素,找出最小值即可,窗口从左到右需要移动n - 1次。这样时间复杂度就变成 O(nk)了;

在暴力枚举的基础上可以发现单调性求最小值最大值时间复杂度为O(n):
如果队列中元素满足 a[i] >= a[i+ 1] (a[i]为队尾元素、a[i + 1]为待比较元素),那么 a[i] 就不会是最小值了,可以直接将 a[i] 删掉;
此时队列中剩下的元素严格单调递增,所以队头就是整个队列中的最小值,可以用 O(1) 的时间找到;

为了维护队列的这个性质,我们在往队尾插入元素之前,先将队尾大于等于当前数的元素全部弹出即可;
这样所有数均只进队一次,出队一次,每次比较的次数可近似为O(1),所以时间复杂度是 O(n) 的。

#include<iostream>

using namespace std;

const int N = 1e6 + 10;
int n, k;
//q中存放的是单调队列的下标
//a中存放的是原数组
int a[N], q[N];

int main()
{
    int hh = 0 , tt = -1;
    cin >> n >> k;
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    
    for(int i = 0; i < n; i ++)
    {
        if(hh <= tt && i - k + 1 > q[hh]) hh ++;    //i-k+1:窗口左边界位置
        while(hh <= tt && a[q[tt]] >= a[i]) tt --; //单调递增顺序入队
        q[++ tt] = i;   //入队列
        
        if(i >= k - 1) cout << a[q[hh]] << ' ';
    }
    puts("");
    
    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) cout << a[q[hh]] << ' ';
    }
    puts("");
    
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值