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

前言

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

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

传统做法

在这里插入图片描述
计算窗口的最大值需要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. 队头出队:当队头从窗口滑出时,队头元素出队(head++); 在这里插入图片描述

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

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

在这里插入图片描述

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

步骤过程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]]);
}

【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()]);
}

【测试代码】

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]]);
 
}

【同理求最小值】

 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]]);
}

备注:参考董晓算法思路

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

热情常在,活力无限

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人间凡尔赛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值