- C++
单调队列-滑动窗口
- 2024-1-28 18:39:20 @
给定一个大小为 n≤106 的数组。
有一个大小为 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 | |
1 3 [-1 -3 5] 3 6 7 | 5 | |
1 3 -1 [-3 5 3] 6 7 | ||
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 7 |
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。
第二行有 n个整数,代表数组的具体数值。
同行数据之间用空格隔开。
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
参考代码:
#include <iostream>
using namespace std;
const int N = 1000010;
int a[N], q[N];
int main()
{
int n, k;
scanf("%d%d", &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 ++ )
{
if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
while (hh <= tt && a[q[tt]] >= a[i]) tt -- ;
q[ ++ tt] = i;
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 ++ ;
while (hh <= tt && a[q[tt]] <= a[i]) tt -- ;
q[ ++ tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
return 0;
}
1 条评论
-
mrhowe SU @ 2024-1-31 21:11:12
定义
顾名思义,单调队列的重点分为「单调」和「队列」。
「单调」指的是元素的「规律」——递增(或递减)。
「队列」指的是元素只能从队头和队尾进行操作。
Ps. 单调队列中的 "队列" 与正常的队列有一定的区别,稍后会提到
例题分析
解释
有了上面「单调队列」的概念,很容易想到用单调队列进行优化。
要求的是每连续的
个数中的最大(最小)值,很明显,当一个数进入所要 "寻找" 最大值的范围中时,若这个数比其前面(先进队)的数要大,显然,前面的数会比这个数先出队且不再可能是最大值。
也就是说——当满足以上条件时,可将前面的数 "弹出",再将该数真正 push 进队尾。
这就相当于维护了一个递减的队列,符合单调队列的定义,减少了重复的比较次数,不仅如此,由于维护出的队伍是查询范围内的且是递减的,队头必定是该查询区域内的最大值,因此输出时只需输出队头即可。
显而易见的是,在这样的算法中,每个数只要进队与出队各一次,因此时间复杂度被降到了
。
而由于查询区间长度是固定的,超出查询空间的值再大也不能输出,因此还需要 site 数组记录第
个队中的数在原数组中的位置,以弹出越界的队头。
过程
例如我们构造一个单调递增的队列会如下:
原序列为:
|
1
|
1 3 -1 -3 5 3 6 7
| | ------------ | ----------------------------- |
因为我们始终要维护队列保证其 递增 的特点,所以会有如下的事情发生:
操作 队列状态 1 入队 {1}
3 比 1 大,3 入队 {1 3}
-1 比队列中所有元素小,所以清空队列 -1 入队 {-1}
-3 比队列中所有元素小,所以清空队列 -3 入队 {-3}
5 比 -3 大,直接入队 {-3 5}
3 比 5 小,5 出队,3 入队 {-3 3}
-3 已经在窗体外,所以 -3 出队;6 比 3 大,6 入队 {3 6}
7 比 6 大,7 入队 {3 6 7}
- 1