両端キューで幅 k の窓ごとの最大値を並べる

毎分の数の並びで、直近 k 分ごとの最大値を全部並べます。値が下がっていく順に添字を両端キューに持ち、窓の外と負けた値を捨てていく、スライディングウィンドウと単調キューの練習です。

海外で定番の問題リスト 4 本には入っていません

問題

あるサイトに 1 分ごとに来た人の数の増減を、整数の並び nums で受け取ります。幅 k の窓 (続いた k 分) を左端から 1 分ずつ右へずらし、窓ごとの最大値を左の窓から順に並べた並びを返す関数を書いてください。

  • 窓は 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 つの窓には 12 が入っています。12 が窓から出たあとは、8 の入った 2 つの窓が続きます。

nums = [4, 2, 12, 3, 8, 1, 6] k = 3
[12, 12, 12, 8, 8]

例 2

幅 1 の窓は値 1 つだけなので、並びがそのまま答えです。

nums = [5, -1, 3] k = 1
[5, -1, 3]

例 3

下がっていく並びでは、窓の左端がいつも最大です。最後の窓だけ、右から入った 6 が最大になります。

nums = [9, 7, 5, 3, 6] k = 2
[9, 7, 5, 6]

面接での聞かれ方

面接では、計算量の条件つきで渡されることがよくあります。

  • 「幅 k の窓をずらしながら、各窓の最大値を返してください。O(n) でできますか?」
  • 「k が 1 のときと、k が n のときは?」と、端の場合を聞かれることがあります
  • ヒープで O(n log n) の答えを言ったあとに、「もっと速くなりませんか?」と詰められることもあります

窓ごとに最大を探し直す書き方、ヒープの書き方、両端キューの書き方の 3 段で話せると、考えの筋が伝わります。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「配列と文字列」に、この型の要点を表とコードでまとめています。

配列と文字列の問題は、全部の組を調べると手間が大きくなる所を、窓や左右の添字で減らす形が多いです。

main.py
def window_max(nums: list[int], k: int) -> list[int]:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(window_max([4, 2, 12, 3, 8, 1, 6], 3))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します