2 本ずつまとめて k 本の並んだ連結リストを 1 本にする

小さい順に並んだ k 本の連結リストを、小さい順のまま 1 本につなぎ直して返します。2 本をまとめる手順を、組み合わせを半分ずつに減らしながら繰り返し、O(N log k) に収める練習です。

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

問題

小さい順に並んだ連結リストの先頭を k 本集めた並び lists を受け取ります。k 本の節を全部使い、小さい順に並んだ 1 本のリストにつなぎ直して、その先頭の節を返す関数を書いてください。

  • 各節は val (値) と next (次の節) を持ちます。最後の節の next は、Python では None、JavaScript では null、Go では nil です。Go の節は ListNode で、Val、Next と大文字で始まります。Go の lists は []*ListNode です
  • どのリストも小さい順に並んでいます。同じ値が何度出てもかまいません
  • lists は空のこともあり、中に空のリストが混じることもあります。節が 1 つも無ければ空のまま返します
  • k は 0 以上 100 以下、それぞれの節の数は 0 以上 50 以下、値はどれも -1000 以上 1000 以下です

例では、リストを先頭から読んだ値の並びで書きます。lists は、その並びを k 本並べた物です。

入力と出力の例

例 1

3 本の先頭は 3、1、4 です。8 は 2 本にあり、両方とも残ります。

lists = [[3, 8, 15], [1, 9], [4, 8, 20]]
[1, 3, 4, 8, 8, 9, 15, 20]

例 2

空のリストは何も足しません。節を持つのは 2 本目だけです。

lists = [[], [6], []]
[6]

例 3

4 本のうち 2 本が 1 つだけの節です。負の数も同じ比べ方で並びます。

lists = [[-5, 0], [-5], [2, 2, 7], [-1]]
[-5, -5, -1, 0, 2, 2, 7]

面接での聞かれ方

2 本をまとめる問題 (merge-sorted-lists) の続きとして、そのまま聞かれることが多い問題です。

  • 「並んだ連結リストが k 本あります。1 本の並んだリストにまとめてください」
  • 「ログを出すサーバーが k 台あり、どれも時刻の順に並んだ記録を持っています。全部を時刻の順に 1 本にしてください」と、場面を付けて聞かれることもあります
  • 書き終えたあとに「優先度付きの待ち行列 (ヒープ) でも書けますか?」「k が 1 万、節の合計が 1 億ならどちらを選びますか?」と、もう 1 つの書き方と比べさせられます

シニアの問題なので、1 本ずつ順に足していく書き方が O(N k) になる理由を言え、O(N log k) に下げる手を自分から出せるかが見られます。

ヒント

まだ開いていません

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

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

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

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

main.py
def merge_k_lists(lists: list[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(merge_k_lists([build_list([3, 8, 15]), build_list([1, 9]), build_list([4, 8, 20])])))

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