用长度k的队列来维护窗口。
先考虑求窗口的最小值,窗口内有数字
a
[
i
]
a[i]
a[i]。
不妨设
a
[
i
]
a[i]
a[i]左边的比它大的数是
a
[
j
]
a[j]
a[j],因为
a
[
j
]
a[j]
a[j]会比
a
[
i
]
a[i]
a[i]先出窗口,那么
a
[
i
]
a[i]
a[i]在的时候窗口最小值不可能是
a
[
j
]
a[j]
a[j]。
因此只要保留
a
[
i
]
a[i]
a[i]和
a
[
i
]
a[i]
a[i]左边比它小的数即可,左边比它大的数可以去掉。
这样队列就是从小到大的单调队列。
具体操作就是
a
[
i
]
a[i]
a[i]从队尾进队时,从队尾向队头扫描,找到第一个比
a
[
i
]
a[i]
a[i]小的数,将
a
[
i
]
a[i]
a[i]放在这个数后面当新的队尾。
窗口最小值就是队头。
同理,求窗口最大值,队列就是从大到小的单调队列。
#include <iostream>
using namespace std;
const int N = 1000005;
int n, k, a[N];
int q1[N], q2[N], h1, t1, h2, t2; // 队列存的是a数组下标
int ans1[N], ans2[N];
int main() {
scanf("%d%d", &n, &k);
h1 = 0, t1 = -1;
h2 = 0, t2 = -1;
for (int i = 1; i <= n; i ++ ) {
scanf("%d", &a[i]);
if (h1 <= t1 && i - k >= q1[h1]) h1 ++ ; // 窗口右移,弹出队头
while(h1 <= t1 && a[i] <= a[q1[t1]]) t1 -- ;
q1[ ++ t1] = i;
if (i >= k) ans1[i - k + 1] = a[q1[h1]];
if (h2 <= t2 && i - k >= q2[h2]) h2 ++ ; // 窗口右移,弹出队头
while (h2 <= t2 && a[i] >= a[q2[t2]]) t2 -- ;
q2[ ++ t2] = i;
if (i >= k) ans2[i - k + 1] = a[q2[h2]];
}
for (int i = 1; i <= n - k + 1; i ++) {
printf("%d ", ans1[i]);
}
printf("\n");
for (int i = 1; i <= n - k + 1; i ++) {
printf("%d ", ans2[i]);
}
printf("\n");
return 0;
}