基本概念:
滑动窗口是以指针为基础的一种思想,两个指针之间形成的集合形成了一个窗口。
例题
describe
给定一个大小为n≤1e6 的数组。
有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k 个数字。
每次滑动窗口向右移动一个位置。
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
分析:
以寻找窗口中的最大值为例
1.窗口的长度为k,我们要找的是窗口中的最大(小)值,且每找到一个窗口会向右移动一个单位
2.我们可以构造一个队列q[ ],以入列和出列的方式模拟窗口的移动。
3.我们发现每当窗口向右移动时我们可以先判断新加进的数是否符合条件,比如说,若目前队尾数为3准备加进来的数为4,那么3就不可能是窗口中的最大值了(鞠躬尽瘁),直接把3踢掉就好了让4替代他的位置;但若加紧来的数比队尾数要小,那么它还是有可能比后边即将加进来的数大的(所以该留下他给他机会让他表现表现)
OK上代码;
#include<iostream>
using namespace std;
int arr[1000001];
int dui1[100001], dui2[100000];
int h = 1, r = 0;
int n, m;
void Max() {
h = 1, r = 0;//h队首,l队尾
for (int i = 1; i <= n; i++) {
while (h <= r && i - dui1[h] >= m)
h++;//查看当前下标和对手下标之间的数是否超过窗口宽度m,如果超过就把队首向右移动
while (h <= r && arr[i] >= arr[dui1[r]])
r--;//查看新来的是否比队尾的数小,如果是那就不用进来了,反正他不可能是最大那个,若比队尾的数大那就把队尾换成它反正队尾不可能是最大的可以退休了
dui1[++r] = i;//储存当前队尾的下标
if (i >= m)cout << arr[dui1[h]] << " ";//输出队首
}
}
void Min()
{
h = 1, r = 0;
for (int i = 1; i <= n; i++) {
while (h <= r && i - dui2[h] >= m)
h++;
while (h <= r && arr[i] <= arr[dui2[r]])//唯一改变的就这个比较大小符号
r--;
dui2[++r] = i;
if (i >= m)cout << arr[dui2[h]] << " ";
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> arr[i];
Min();
cout << endl;
Max();
return 0;
}