154. 滑动窗口
给定一个大小为 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 | 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 |
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数 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 maxn = 1e6+10;
int a[maxn];
int q[maxn],head,tail;
int main()
{
int n,k;
cin >> n >> k;
for(int i=0;i<n;++i)
cin >> a[i];
head=0,tail=-1;
//队列数据存储下标信息,方便队首与第i个位置的数据进行比较
for(int i=0;i<n;++i)
{
if(head<=tail && q[head]<=i-k) //如果队首的下标位置是在i的前k个位置,
++head; //进行出队,后面是不会再用到的
while(head<=tail && a[q[tail]]>=a[i]) //单调递增队列
--tail;
q[++tail]=i;
if(i-k+1>=0)
cout << a[q[head]] << ' '; //队首是最小元素的下标
}
puts("");
head=0,tail=-1;
for(int i=0;i<n;++i)
{
if(head<=tail && q[head]<=i-k) //如果队首的下标位置是在i的前k个位置,
++head; //进行出队,后面是不会再用到的
while(head<=tail && a[q[tail]]<=a[i]) //单调递减队列
--tail;
q[++tail]=i;
if(i-k+1>=0)
cout << a[q[head]] << ' '; //队首是最大元素的下标
}
return 0;
}