半分に分けて後ろを逆にし、連結リストを前と後ろから互い違いに並べ直す

連結リストを、先頭、最後、2 番目、後ろから 2 番目、と前と後ろから交互に取る順につなぎ直します。まん中を探す、後ろ半分を逆にする、2 本を交互につなぐ、の 3 つを組み合わせる練習です。

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

問題

連結リストの先頭 head を受け取ります。節を、前から 1 番目、後ろから 1 番目、前から 2 番目、後ろから 2 番目、と交互に取る順につなぎ直し、先頭の節を返す関数を書いてください。

  • 節が L0、L1、…、Ln の順なら、つなぎ直したあとは L0、Ln、L1、Ln−1、L2、Ln−2、… の順です
  • 節の値を書き換えず、節のつなぎ先を付け替えてください。新しい節は作りません
  • 各節は val (値) と next (次の節) を持ちます。最後の節の next は、Python では None、JavaScript では null、Go では nil です。Go の節は ListNode で、Val、Next と大文字で始まります
  • 節の数は 1 以上 5000 以下、値はどれも 1 以上 1000 以下です
  • 先頭の節は変わらないので、受け取った head をそのまま返します

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

入力と出力の例

例 1

発表の順番を、最初の人、最後の人、2 番目の人、と前後から交互に呼ぶ順に並べ直します。節が 5 つなので、まん中の 30 が最後に来ます。

head = [10, 20, 30, 40, 50]
[10, 50, 20, 40, 30]

例 2

節が偶数のときは、前から取った節と後ろから取った節が同じ数になります。

head = [1, 2, 3, 4, 5, 6]
[1, 6, 2, 5, 3, 4]

例 3

節が 2 つなら、並びは変わりません。

head = [7, 8]
[7, 8]

面接での聞かれ方

連結リストの基本の操作を 3 つ組み合わせる問題として、ミドルの面接で出ます。

  • 「連結リストを、前と後ろから交互に取る順に並べ直してください」
  • 並びに移してから並べ直すと、「追加の記憶を O(1) にできますか?」と聞かれます
  • 3 つの段 (まん中を探す、逆にする、交互につなぐ) を関数に分けて書くと、読みやすさの点でも話が広がります

ミドルの問題なので、前半と後半の切り離しを忘れて輪ができないか、節が奇数と偶数のどちらでも動くかが見られます。

ヒント

まだ開いていません

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

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

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

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

main.py
def reorder_list(head: ListNode | None) -> 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(reorder_list(build_list([10, 20, 30, 40, 50]))))

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