滑动窗口 一道单调队列的理解模板题

本文介绍如何使用单调队列解决滑动窗口的最大值和最小值问题,通过实例讲解了单调队列的原理和应用,包括队列的增加、删除操作及如何保持队列单调性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先来看这个题,理解一下题意,我再来介绍单调队列

F. 滑动窗口

Description
上古文明遗迹中,有一个长度为k的滑动窗口从数组的左端滑到右端,试输出窗口每次移动时窗口里能看到的最大值和最小值。例如有数组为{1,3,-1,-3,5,3,6,7},窗口长度为3,则输出的最大最小值如表
在这里插入图片描述

Input
第一行为两个整数,即n和k(1<n≤1000000)。
第二行为n个整数。

Output
第一行为最小数,第二行为最大数(每行行末均有一空格)。

Samples
Input Copy
8 3
1 3 -1 -3 5 3 6 7
Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7

首先要介绍一下单调队列
我们都知道队列是一种先进先出的数据结构
这就类似于我们在餐厅排队买饭,先排队的人总是先买到饭然后离开这个队列
那么单调队列就是符合队列先进先出特点并且队列元素是单调的数据结构

单调队列可以用数组和指针实现
我们定义一个数组 que[maxn] 来储存队列的下标,定义两个指针 head = 1,tail = 0 来表示头和尾

我们都知道队列有增加、删除这样的操作,对于单调队列来说,如果要增加一个元素,就是把这个元素放到队尾,那么我们直接给指针 tail++ ,然后把新的下标存储下来;如果要删除一个元素,就是让队首元素出队,那么就可以直接让指针 head–,向后指一位即可。

void add(){
	tail++;
}

void erase(){
	head--;
}

单调队列总是满足这样的性质
假设构造递增的单调队列

[i < j]  a[i] < a[j]
[i < j]  a[i] >= a[j]   不存在

所以我们就可以利用这样的性质去构造单调队列
当 head <= tail 并且 a[que[tail]] >= a[i] 的时候,如果把 a[i] 加入队列就不符合单调的性质,但是这个时候显然把单调队列的最后一个元素替换成现在的 a[i] 更优,所以我们更新当前的单调队列。递减的单调队列同理,把 >= 换成 <= 即可。

现在我们就可以做这道题了

int que[maxn], head = 1, tail = 0;
int n,k,a[maxn];

int main(){
	
	io >> n >> k;
	for(int i=1;i<=n;i++) io >> a[i];

	for(int i=1;i<=n;i++){
		while(head <= tail && a[que[tail]] >= a[i]) tail--;
		que[++tail] = i;
		while(i - que[head] + 1 > k) head++;
		if(i >= k) printf("%d ",a[que[head]]);
	}
	puts("");

	head = 1, tail = 0;
	for(int i=1;i<=n;i++){
		while(head <= tail && a[que[tail]] <= a[i]) tail--;
		que[++tail] = i;
		while(i - que[head] + 1 > k) head++;
		if(i >= k) printf("%d ",a[que[head]]);
	}
	puts("");
	return 0;
}
### 单调队列滑动窗口问题中的应用 单调队列用于解决特定类型的滑动窗口问题,特别是在需要频繁查询区间最值的情况下表现出色。该结构不仅支持标准的队列入队和出队操作,而且能确保内部数据按照一定顺序排列——通常是单调递增或递减的形式[^2]。 #### 算法伪代码模板 为了实现高效的滑动窗口最大/最小值计算,可以采用以下伪代码作为基础框架: ```plaintext for each element in the array: while queue is not empty and current_element violates monotonicity condition of queue: remove elements from rear end of queue add index of current_element to rear end of queue if front element of queue falls outside window bounds: remove it from front once we have processed at least 'k' elements (window size), start recording results using frontmost element. ``` 这段逻辑保证了每当遍历新元素时,都会调整队列以维护其单调性质;同时,在达到所需窗口大小之后,始终利用位于队头位置的那个索引来访问当前窗口内的极值[^1]。 #### Python 示例代码 下面是一个具体的Python实现例子,展示了如何运用上述原理来求解给定数组中每个长度为`k`的子序列的最大值: ```python from collections import deque def maxSlidingWindow(nums, k): result = [] q = deque() # Stores indices with decreasing values. for i in range(len(nums)): # Remove out-of-bound indices from the front. if q and q[0] <= i - k: q.popleft() # Maintain decreasing order by removing smaller numbers at back. while q and nums[q[-1]] < nums[i]: q.pop() q.append(i) # Start collecting answers after processing first complete window. if i >= k - 1: result.append(nums[q[0]]) return result if __name__ == "__main__": test_case = ([1,3,-1,-3,5,3,6,7], 3) print(maxSlidingWindow(*test_case)) ``` 此程序片段定义了一个函数 `maxSlidingWindow`, 它接收两个参数:一个是整型列表 `nums` 表示待处理的一维数组,另一个是正整数 `k` 指明滑动窗口宽度。通过构建并更新一个双端队列 `q` 来追踪可能成为候选最大值的位置,并最终返回这些最大值组成的列表[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

你数过天上的星星吗

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

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

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

打赏作者

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

抵扣说明:

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

余额充值