給定長度 n ≤ 10⁶ 的數組和窗口大小 k,窗口從左向右滑動,每次移動一位。輸出每個窗口位置的最小值和最大值。
樣例:
數組:[1 3 -1 -3 5 3 6 7], k=3 窗口最小值:-1 -3 -3 -3 3 3 窗口最大值:3 3 5 5 6 7
思想:維護一個單調遞增(求最小)或單調遞減(求最大)的隊列,隊列存儲數組下標。
a[i] 入隊前,從隊尾彈出所有 ≥ a[i] 的元素(它們不可能再成為最小值)i 入隊≤ i-k(滑出窗口),彈出隊首同理,彈出隊尾所有 ≤ a[i] 的元素。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1000000 + 10;
int q[MAXN], a[MAXN];
int n, k;
void solve() {
// --- 求最小值 ---
int front = 0, rear = 0;
q[++rear] = 1; // 1-based 下標
for (int i = 1; i <= n; ++i) {
// 1. 維護單調遞增:彈出隊尾 ≥ a[i]
while (front <= rear && a[q[rear]] >= a[i]) --rear;
q[++rear] = i;
// 2. 彈出滑出窗口的隊首
while (q[rear] - q[front] + 1 > k) ++front;
// 3. 輸出
if (i == k) printf("%d", a[q[front]]);
else if (i > k) printf(" %d", a[q[front]]);
}
puts("");
// --- 求最大值 ---
front = 0; rear = 0;
q[++rear] = 1;
for (int i = 1; i <= n; ++i) {
while (front <= rear && a[q[rear]] <= a[i]) --rear;
q[++rear] = i;
while (q[rear] - q[front] + 1 > k) ++front;
if (i == k) printf("%d", a[q[front]]);
else if (i > k) printf(" %d", a[q[front]]);
}
puts("");
}
int main() {
while (scanf("%d%d", &n, &k) != EOF) {
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
solve();
}
return 0;
}
q[rear] - q[front] + 1O(1),總體 O(n)O(n),n ≤ 10⁶ 輕鬆通過O(n) 隊列與數組單調隊列是滑動窗口極值問題的標準解法,將暴力 O(nk) 優化至 O(n)。關鍵在於「新元素入隊時淘汰無用元素」與「隊首滑出窗口時彈出」兩條規則,保證隊列始終單調且僅含窗口內候選極值。