つなぎ先の付け替えで連結リストを k 個ずつ逆順にする

連結リストを先頭から k 個ずつの組に分け、それぞれの組の中を逆順につなぎ直します。k 個に足りない最後の残りはそのままです。組の前後のつなぎ目を見失わずに付け替える練習です。

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

問題

連結リストの先頭 head と整数 k を受け取ります。リストを先頭から k 個ずつの組に分け、それぞれの組の中の節を逆順につなぎ直して、新しい先頭の節を返す関数を書いてください。

  • 最後に k 個に足りない節が残ったら、その残りは元の順のままにします
  • 節の値を書き換えず、節のつなぎ先を付け替えてください
  • 各節は val (値) と next (次の節) を持ちます。最後の節の next は、Python では None、JavaScript では null、Go では nil です。Go の節は ListNode で、Val、Next と大文字で始まります
  • 節の数は 1 以上 5000 以下、k は 1 以上、節の数以下です。値はどれも 0 以上 1000 以下です
  • 追加の記憶は O(1) にしてください

例では、リストを先頭から読んだ値の並びで書きます。

入力と出力の例

例 1

3 個ずつに分けると、1、2、3 と 4、5、6 の 2 組と、残りの 7、8 です。2 組をそれぞれ逆にし、残りの 2 個はそのままです。

head = [1, 2, 3, 4, 5, 6, 7, 8] k = 3
[3, 2, 1, 6, 5, 4, 7, 8]

例 2

2 個ずつなら、隣どうしが入れ替わります。節が 4 つでちょうど割り切れるので、残りはありません。

head = [10, 20, 30, 40] k = 2
[20, 10, 40, 30]

例 3

k が 1 なら、どの組も 1 個なので、何も変わりません。

head = [5, 6, 7] k = 1
[5, 6, 7]

面接での聞かれ方

連結リストの付け替えの難しい問題として、シニアの面接で出ます。

  • 「連結リストを k 個ずつ逆にしてください。足りない残りはそのままです」
  • 連結リストを逆にする問題 (この一覧の reverse-linked-list) を書いたあとの続きとして出されることがよくあります
  • 再帰で書くと短くなりますが、「追加の記憶を O(1) にできますか?」と繰り返しの書き方を求められることがあります

シニアの問題なので、組の前の節と次の組の先頭の 2 つのつなぎ目を、図を描きながら正しく付け替えられるかが見られます。

ヒント

まだ開いていません

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

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

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

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

main.py
def reverse_k_group(head: ListNode | None, k: int) -> ListNode | None:
    """
    Goal: 何を返すか

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

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

    edge case: 空、1 つだけ、など
    """
    # 連結リストの節は val、next を持ちます。最後の next は None です
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
# build_list は並びから連結リストを組みます
print(list_values(reverse_k_group(build_list([1, 2, 3, 4, 5, 6, 7, 8]), 3)))

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