|
|
|
|
##[$AcWing$ $154$ 滑动窗口](https://www.acwing.com/problem/content/156/)
|
|
|
|
|
|
|
|
|
|
### 一、题目描述
|
|
|
|
|
|
|
|
|
|
有一个大小为$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$ |
|
|
|
|
|
|
|
|
|
|
您的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
|
|
|
|
|
|
|
|
|
|
### 二、理解和感悟
|
|
|
|
|
|
|
|
|
|
下面以求窗口中最小值为例,进行说明:
|
|
|
|
|
|
|
|
|
|
1、维护一个队列,来一个新人,将队列中大于它的老家伙们干死,保留比它小的老家伙们。
|
|
|
|
|
|
|
|
|
|
2、道理:
|
|
|
|
|
(1)老家伙比新人还大,新人又小活的时间又长,老家伙永远也不可能为后面提供帮助了,所以干死~
|
|
|
|
|
`tt--`
|
|
|
|
|
(2)不管是不是更小,只要寿命到了,也一样要死。
|
|
|
|
|
`hh++`
|
|
|
|
|
|
|
|
|
|
3、其实,这本身是一个双端队列,不是传统的队列,出队的可能是队头,也可能是队尾。
|
|
|
|
|
|
|
|
|
|
求窗口中的最大值正好与之相反,小修改一下即可。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 三、C++代码
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
const int N = 1000010;
|
|
|
|
|
int n;
|
|
|
|
|
int k;
|
|
|
|
|
int a[N];
|
|
|
|
|
int q[N], hh, tt;
|
|
|
|
|
int main() {
|
|
|
|
|
cin >> n >> k;
|
|
|
|
|
for (int i = 1; i <= n; i++) cin >> a[i];
|
|
|
|
|
|
|
|
|
|
// 初始化队列
|
|
|
|
|
hh = 0, tt = -1;
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
// Q1:队列里面放的是什么?
|
|
|
|
|
// A1:是数组下标,是编号,不是值,需要值时,可以通过a[q[hh]]去取值
|
|
|
|
|
|
|
|
|
|
// Q2:队列的存入形态是什么样的?
|
|
|
|
|
// A2: hh....tt,hh在左,tt在右
|
|
|
|
|
|
|
|
|
|
// Q3:什么样的需要出队列?
|
|
|
|
|
// A3: (1)距离当前位置超过窗口范围
|
|
|
|
|
// (2) 在窗口范围内,但对后续没有可能发挥作用:区间内的人员,有比你更年轻、更漂亮的,怎么选美也选不到你
|
|
|
|
|
while (hh <= tt && i + 1 - k > q[hh]) hh++; // q[hh]:窗口的左起点,hh++:减小窗口长度
|
|
|
|
|
while (hh <= tt && a[q[tt]] >= a[i]) tt--; // tt--:减小窗口长度
|
|
|
|
|
// q[],hh,tt 三者组成了一个模拟的队列数据结构,对外提供:查询队列中最小值位置的服务q[hh],对内三者互相协作
|
|
|
|
|
// 换言之:q[hh]是对外的,hh,tt是数据结构内部概念,不能混淆
|
|
|
|
|
q[++tt] = i;
|
|
|
|
|
// 只有在区间长度够长的情况下,才能输出区间最小值
|
|
|
|
|
if (i >= k) printf("%d ", a[q[hh]]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
puts("");
|
|
|
|
|
|
|
|
|
|
hh = 0, tt = -1;
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
while (hh <= tt && i + 1 - k > q[hh]) hh++;
|
|
|
|
|
while (hh <= tt && a[q[tt]] <= a[i]) tt--;
|
|
|
|
|
q[++tt] = i;
|
|
|
|
|
if (i >= k) printf("%d ", a[q[hh]]);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|