• 【每日一题(滑动窗口)】


    前言

    **单调队列:**队列内的元素是单调的,递增或者递减。
    本题用单调队列存储当前窗口内单调递减的元素。队列从队头到队尾对应窗口内从最大值到尾元素的一个子序列。

    单调队列–滑动窗口最大值

    传统做法

    在这里插入图片描述
    计算窗口的最大值需要O(m),移动n-m+1次,时间复杂度O(nm)。
    【代买演示】

    for(int i = m;i<n;i++){
    	int maxn=a[i];
    	for(int j=i-m+1;j <= i ;j++){
    	maxn=max(maxn,a[j]);
      }
      printf("%d",maxn);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    优化

    1. 队头出队:当队头从窗口滑出时,队头元素出队(head++); 在这里插入图片描述

    3. 队尾入队:当新元素滑入队尾时,要把新元素从队尾插入,分两种情况:
    1. 直接插入:如果新元素小于队尾元素,那直接从队尾插入(++tail),因为它可能在前面的最大值滑出窗口后成为最大值。
    在这里插入图片描述

    2. 先删后插:如果新元素大于等于队尾元素,那就先删除队尾元素,因为他不可能成为窗口中最大值,删除方法`tail--`,即从队尾出队。循环删除,直到队空或者遇到一个大于新元素的值,出入其后`(++tail)`。
    
    • 1

    在这里插入图片描述

    注意:队列应该存储窗口元素的下标值,便于判断队头出队
    在这里插入图片描述

    步骤过程q[h]下标值最大值
    i=1 [2 ]6 4 9 8 5 5 22窗口不够长
    i=2[2 6] 4 9 8 5 5 26窗口不够长
    i=3[2 6 4] 9 8 5 5 26 46
    i=42 [6 4 9] 8 5 5 299
    i=52 6 [4 9 8] 5 5 29 89
    i=62 6 4 [9 8 5] 5 29 8 59
    i=72 6 4 9 [8 5 5] 28 58
    i=82 6 4 9 8 [5 5 2]5 25

    【代码演示】

    int h  = 0,t=-1;
    for(int i = 0; i<n; i++){
    //如果q[h]不在窗口[i-m+1,i]内,队头出队
    if(h<=t && q[h]< i-m+1 )h++;
    //当前值>= 队尾值,队尾出队
    while(h<=t && a[i] >= a[q[h]])t--;
    //下标入队,下标加到队尾
    q[++t]=i;
    //使用队头最大值,规定窗口长度
    if(i>m-1)printf("%d",a[q[h]]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    【STL模拟代码演示】

    queue<int>q;//双向队列
    for(int i = 1;i<=n;i++){
    	//判断队头是否需要出队
    	if(!q.empty() && q.front()<i-m+1)q.pop_front();
    	//维护队列单调性
    	while(!q.empty() && a[i]>= a[q.back()])
    		q.pop_back();
    	//下标入队,便于队头出队
    	q.push_back(i);
    	//取队头最为窗口最大值
    	if(i>= m-1)printf("%d",a[q.front()]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    【测试代码】

    int h  = 0,t=-1;
    for(int i = 0; i<n; i++){
    printf(" i =%d\n q[%d]=%d q[%d]=%d\n",i,h,q[h],t,q[t]);
    //如果q[h]不在窗口[i-m+1,i]内,队头出队
    if(h<=t && q[h]< i-m+1 )h++,printf("1 head = %d\n",h);
    //当前值>= 队尾值,队尾出队
    while(h<=t && a[i] >= a[q[h]])t--;
    q[++t]=i,printf(" 2 tail =%d\n",t);
    //使用队头最大值
    if(i>=m-1)printf("maxn= %d\n",a[q[h]]);
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    【同理求最小值】

     for(int i = 0; i<n; i++){
        //如果q[h]不在窗口[i-m+1,i]内,队头出队
        if(h<=t && q[h]< i-m+1 )h++;
        //当前值>= 队尾值,队尾出队
        while(h<=t && a[i] <= a[q[t]])t--;//改变最小的下标
        
        //下标入队
        q[++t]=i;
        //使用队头最大值,规定窗口长度
        if(i>= m-1)printf("%d ",a[q[h]]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    备注:参考董晓算法思路

    总结:如果对你有帮助动动手指一键三连吧!

    热情常在,活力无限

  • 相关阅读:
    金融业务系统: Service Mesh用于安全微服务集成
    [附源码]SSM计算机毕业设计朋辈帮扶系统JAVA
    python reportlab 生成多页pdf
    SpringMVC01
    C语言关于自定义字符函数和字符串函数的相关笔试题(找工作必看)
    静态HTML旅行主题网页作业——青岛民俗7页html+css+javascript+jquery 地方民俗网页设计与实现
    MySQL-运维篇 初识
    Unescaped & or nonterminated character/entity reference 报错-IDEA
    k8s-重启策略和健康检查
    不惧繁杂背景,视频编辑服务一键实现人像抠图
  • 原文地址:https://blog.csdn.net/m0_53142039/article/details/126088720