单调队列 滑动窗口(题目分析+C++完整代码)

发布于:2025-02-10 ⋅ 阅读:(30) ⋅ 点赞:(0)

滑动窗口/单调队列

原题链接
AcWing 154. 滑动窗口

题目描述
给定一个数组。
有一个大小为 k的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k个数字。
每次滑动窗口向右移动一个位置。

以下是一个例子:
该数组为 [1 3 -1 -3 5 3 6 7],k为 3。

窗口位置 最小值 最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7

任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
题目分析
窗口可用队列来维护,队尾插入,队头弹出。
若求最小值,
如下列实例滑动窗口中,3<-3,-1<-3,且-3在后面,则只要-3在,则最小值一定是-3,3与-1的值一定不会被取到,
在这里插入图片描述
因此,提炼出,只要出现逆序对,即前面的数大于后面的数,则一定不会取到,则出队
由此,可知,队列中的数一定是单调递增的( 单调队列 )。
则,队头存储的即为当前的最小值

(可仔细看下方实例加深理解!!!)
在这里插入图片描述
求最大值同理。

完整代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N=1000010;
int n,k;
int a[N];  //存储原数组
int q[N];  //数组模拟队列,存储数组下标
int main(){
    scanf("%d%d",&n,&k);  //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++){
        //hh<=tt表明队列不为空
        
        //判断队头是否已经滑出窗口
        //窗口最前端的数组下标大于队首,则队头数出列
        if(hh<=tt&&i-k+1>q[hh]) hh++;  //i-k+1(当前数组下标i-窗口长度k+1)为当前窗口最前端的数组下标
        
        //判断队尾数组是否大于当前数,若大于,出现逆序,则队尾的数弹出tt--
        while(hh<=tt&&a[q[tt]]>=a[i]) tt--;
        q[++tt]=i;  //当前数入队
        
        //前k-1个数时,不输出答案
        if(i>=k-1) printf("%d ",a[q[hh]]);
    }
    puts("");
    
    //最大值
    hh=0,tt=-1;
    for(int i=0;i<n;i++){
        //判断队头是否已经滑出窗口
        //窗口最前端的数组下标大于队首,则队头数出列
        if(hh<=tt&&i-k+1>q[hh]) hh++;  //i-k+1(当前数组下标i-窗口长度k+1)为当前窗口最前端的数组下标
        
        //判断队尾数组是否小于当前数,若小于,出现逆序,则队尾的数弹出tt--
        while(hh<=tt&&a[q[tt]]<=a[i]) tt--;
        q[++tt]=i;  //当前数入队
        
        //前k-1个数时,不输出答案
        if(i>=k-1) printf("%d ",a[q[hh]]);
    }
    
}

网站公告

今日签到

点亮在社区的每一天
去签到