1:滑动窗口
查看提交统计提问
总时间限制: 12000ms 内存限制: 65536kB
描述
给定一个长度为n(n<=10^6)的数组。有一个大小为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个数字。
输出
输出包括两行。
第一行包括窗口从左至右移动的每个位置的最小值。
第二行包括窗口从左至右移动的每个位置的最大值。
思路:不能采用直接比较的方法,会超出时间限制。要使用优先对列来实现。
以求最小值为例,维护这样一个队列:
1.队列中元素保存数列下标,数列中元素(下标)递增,并且下标对应数列中元素(下标对应值)也递增。
显然我们i从0开始遍历保证了队列中保存的下标是递增的,我们只需要设计算法保证下标对应数列中元素也递增即可。
2.加入一个下标时,从后往前删掉所有对应值大于当前下标对应值的下标,使得下标对应元素也能递增。
3.将这个下标加入队列
4.如果队列首位元素在后面不再需要用到了,队列首位后移一位。
#include<iostream>
using namespace std;
int n, k;
int a[1000000], b[1000000];
void min()
{
int uut = 1, r = 0;
for (int i = 1; i <= n; i++) {
while (r >= uut && a[b[r]] >= a[i])//加入新下标,先从后往前删除所有大于的元素下标
r--;
b[++r] = i;
if (b[r] - b[uut] + 1 > k) //b[uut]存放最小值的下标
uut++;
if (i >= k) //输出
cout<<a[b[uut]]<<" ";
}
cout << endl;
}
void max()
{
int l = 1, r = 0;
for (int i = 1; i <= n; i++) {
while (r >= l && a[b[r]] <= a[i])
r--;
b[++r] = i;
if (b[r] - b[l] + 1 > k)
l++;
if (i >= k)
cout << a[b[l]] << " ";
}
cout << endl;
}
int main()
{
cin >>n >> k;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
min();
max();
return 0;
}