一、题目描述
有一个大小为\(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++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
int n; //数组长度
int k; //窗口长度
int a[N]; //原始数组
int q[N]; //队列,内容是原始数组的下标
int main() {
//优化输入
ios::sync_with_stdio(false);
//读入数据
cin >> n >> k;
for (int i = 1; i <= n; i++)cin >> a[i];
//从左至右,每个位置滑动窗口中的最小值。
int tt = -1, hh = 0;
for (int i = 1; i <= n; i++) {
//队列中数据有两种方式死亡:
//1、到年龄了,该死了。
if (hh <= tt && i + 1 - k > q[hh]) hh++;
//2、没到年龄,但有后面的小孩比你牛,活的还比你长,岁数大的就没有保存下去的必要了,死了得了。
while (hh <= tt && a[q[tt]] >= a[i]) tt--;
//加入队伍
q[++tt] = i;
//窗口够长后,才能输出窗口内最小值,队头就是最小值。(单调队列)
if (i >= k) printf("%d ", a[q[hh]]);
}
//输出换行
puts("");
//从左至右,每个位置滑动窗口中的最大值。
tt = -1, hh = 0;
for (int i = 1; i <= n; i++) {
if (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;
}