両端キューで 幅 k の 窓ごとの 最大値を並べる
毎分の
海外で定番の問題リスト 4 本には入っていません
問題
あるnums でk のk 分) を
- 窓は
nums[0]からnums[k - 1]が最初で、 nums[n - k]からnums[n - 1]が最後です。 返す 並びの 長さは n - k + 1 です numsの長さ n は 1 以上 100000 以下で、 値は -10000 以上 10000 以下です kは 1 以上 n 以下です
入力と出力の例
例 1
最初の 3 つの
例 2
幅 1 の
例 3
下がっていく
面接での聞かれ方
面接では、
- 「幅 k の
窓を ずらしながら、 各窓の 最大値を 返してください。 O(n) で できますか?」 - 「k が 1 の
ときと、 k が n の ときは?」と、 端の 場合を 聞かれる ことがあります - ヒープで O(n log n) の
答えを 言った あとに、 「もっと 速くなりませんか?」と 詰められる こともあります
窓ごとに
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「配列と文字列」に、
関わる学び
配列と
まず 考える手順
段 1
素直にk 個の
段 2
最大を
段 3
大事な
段 4
キューのi - k 以下
解答
Python
Python
from collections import deque
def window_max(nums, k):
q = deque()
out = []
for i, x in enumerate(nums):
while q:
if nums[q[-1]] > x:
break
q.pop()
q.append(i)
if q[0] <= i - k:
q.popleft()
if i >= k - 1:
m = nums[q[0]]
out.append(m)
return outJavaScript
JavaScript
function windowMax(
nums,
k
) {
const q = [];
let head = 0;
const out = [];
const n = nums.length;
for (let i = 0; i < n; i++) {
const x = nums[i];
while (
q.length > head &&
nums[q[q.length - 1]] <= x
) {
q.pop();
}
q.push(i);
if (q[head] <= i - k) {
head++;
}
if (i >= k - 1) {
out.push(nums[q[head]]);
}
}
return out;
}Go
Go
func windowMax(
nums []int,
k int,
) []int {
q := []int{}
out := []int{}
for i, x := range nums {
for len(q) > 0 &&
nums[q[len(q)-1]] <= x {
q = q[:len(q)-1]
}
q = append(q, i)
if q[0] <= i-k {
q = q[1:]
}
if i >= k-1 {
m := nums[q[0]]
out = append(out, m)
}
}
return out
}q にはx 以下なら、head でhead を
計算量
- 時間は O(n) です。
どの 添字も、 キューに 入るのが 1 回、 出るのが 多くて 1 回です。 右から 捨て 続ける 繰り返しも、 全体で n 回を 越えません - 使う
記憶は O(k) です。 キューには 窓の 中の 添字しか 残りません。 答えの 並びの n - k + 1 個は 数えていません。 ただし JavaScript の 解答は、 headより前の 使い 終わった 添字を 配列から 消さないので、 配列は O(n) まで 伸びます。 O(k) に 収めたい ときは、 headが配列の 半分を 越えたら前を 切り詰めます - 段 1 の
窓ごとに 探し直す 書き方は O(n × k)、 段 2 の ヒープは O(n log n) です
よく ある間違い
- キューに
添字ではなく 値を入れる。 同じ 値が いくつも あると、 どれが 窓から 出たのか分から なくなります。 添字を 入れ、 値は numsから読みます - 右から
捨てる 条件を <にして、 同じ値を残す。 答えは 変わりませんが、 同じ 値が 続く 並びで、 キューが 窓の 幅まで 伸びます。 <=で捨てても、 新しい 方が 長く 残るので 困りません - 左の
端が 窓の 外に 出たかを 確かめ忘れる。 窓の 外の 古い 最大を 答えに 入れます。 例 1 の 4 つ目の 窓 [3, 8, 1]で、外に 出た 12 を 返してしまいます - 最初の
窓が そろう 前から 答えに足す。 i >= k - 1になるまでは 足しません。 足すと、 答えの 並びが k - 1 個長くなります
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「右から
大きい 値が 入ると、 それより 左の 小さい 値は もう 最大に なりません。 そこで、 最大に なる 見込みの ある 添字だけを、 値の 下がる 順に 両端キューに 持ちます。 入れる 前に 右の 端から それ以下の 値を 捨て、 左の 端が 窓の 外なら 捨てます。 左の 端が 窓の 最大です。 どの 添字も 1 回入って 多くて 1 回出るので、 時間は O(n) です。」
書き終えたら、
「例 1 で
たどります。 4、 2 と 入れ、 12 で 両方捨てて 12 だけ。 最初の 窓の 最大は 12 です。 3 は 12 より 小さいので 後ろに 足し、 8 では 3 を 捨てます。 12 は まだ窓の 中なので 12 が 続きます。 1 を 足した あと、 12 の 添字 2 が 窓の 外に なって 捨て、 8 が 最大です。 6 では 1 を 捨て、 8 が 残って 最大は 8 です。」