ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
Input
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
Output
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7
实现思路:
由于需要在n个数值中找到每连续k个数值中的最大值和最小值,所以定义两个单调栈,一个单调增(Qmax),一个单调减(Qmin),分别用来存储最大值和最小值的位置.
最小值求取:先将第一个元素入队,i表示当前将要入队的元素位置,在下一元素入队前,将栈中所有小于它的值弹出,直至i>k,将当前最小值存入b[1]中,
再将i变换到k+1,在i<=n时重复上述操作,若i-k=Qmin.back(),即当当前要入队的元素位置到第一个入队的元素位置为k+1时,为保证窗口为k,删除队中第一个元素,并将当前最小值存入数组b中,继续上述操作。
最大值与最小值求取类似。
实现代码:
#include<cstdio>
#include<iostream>
#include<queue>
const int maxn = 1e7;
int a[maxn];
int b[maxn];
int c[maxn];
using namespace std;
int main()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i++)
scanf_s("%d", &a[i]);
deque<int>Qmin;
deque<int>Qmax;
//求最小值
for (int i = 1; i <= k; i++)
{
while (!Qmin.empty())
{
int x = Qmin.front();
if (a[x] > a[i])
Qmin.pop_front();
else
break;
}
Qmin.push_front(i);
}
b[1] = a[Qmin.back()];
int s = 1;
for (int i = k + 1; i <= n; i++)
{
while (!Qmin.empty())
{
int x = Qmin.front();
if (a[x] > a[i])
Qmin.pop_front();
else
break;
}
Qmin.push_front(i);
if (i - k == Qmin.back()) {
Qmin.pop_back();
}b[++s] = a[Qmin.back()];
}
//求最大值
for (int i = 1; i <= k; i++)
{
while (!Qmax.empty())
{
int y = Qmax.front();
if (a[y] < a[i])
Qmax.pop_front();
else
break;
}Qmax.push_front(i);
}
c[1] = a[Qmax.back()];
int h = 1;
for (int i = k + 1; i <= n; i++)
{
while (!Qmax.empty())
{
int y = Qmax.front();
if (a[y] < a[i])
Qmax.pop_front();
else
break;
}
Qmax.push_front(i);
if (i - k == Qmax.back()) {
Qmax.pop_back();
}c[++h] = a[Qmax.back()];
}
for (int i = 1; i <= s; i++)
{
printf("%d ", b[i]);
}
printf("\n");
for (int i = 1; i <= h; i++)
{
printf("%d ", c[i]);
}
//system("pause");
return 0;
}
小结:在实现左右都需要进行操作的时候可以采用双向队列,且单调队列可以较快实现固定长度数值的插入和删除。