幅優先探索 (BFS) と辞書でつながりのグラフを写し取る

グラフ問題 114

1 つの節から届くグラフを、節を 1 回ずつだけ写して、つながりごと写し取ります。元の節と写した節の対応を辞書に持ち、初めて見た節だけを写して列に並べる練習です。

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

問題

社内の連絡網を、新しいシステムへ写します。古いシステムでは、人に 0 からの番号が付いていて、adj[i] が i 番の人と直接つながっている人の番号の並びです。つながりは両向きで、i 番の並びに j があれば、j 番の並びにも i があります。

新しいシステムへは、start 番の人から連絡網をたどって届く人だけを写します。写した人には、新しい番号を次の決まりで 0 から振ります。

  • start 番の人を新しい 0 番にします
  • そこから幅優先探索 (BFS) で人をたどります。ある人の隣を見る順は、adj の並びの順です。初めて見た人に、次の新しい番号を振ります

写したあとの連絡網を、同じ形 (新しい番号の i 番の隣を、新しい番号で、元と同じ順に並べた並びの並び) で返す関数を書いてください。

  • 人の数は 1 以上 100 以下です。自分自身とのつながりや、同じ 2 人の 2 重のつながりはありません
  • start は 0 以上、人の数より小さい整数です

入力と出力の例

例 1

0、1、3、2 が輪につながっています。3 番から始めると、3 が新しい 0、隣の 1 と 2 が新しい 1 と 2、最後に見つかる 0 が新しい 3 です。

adj = [[1, 2], [0, 3], [0, 3], [1, 2]] start = 3
[[1, 2], [3, 0], [3, 0], [1, 2]]

例 2

人が 1 人で、つながりがありません。

adj = [[]] start = 0
[[]]

例 3

0 と 1、2 と 3 が別々のまとまりです。2 番から届くのは 2 と 3 だけなので、2 人だけを写します。

adj = [[1], [0], [3], [2]] start = 2
[[1], [0]]

面接での聞かれ方

面接では、短く次のように渡されることが多いです。

  • 「無向グラフの 1 つの節が渡されます。グラフ全体を深く写して (ディープコピーして) 返してください」
  • 節はクラス (値と、隣の節の並び) で渡され、写した節を返す形が多いです。ここでは頁で値を受け渡すため、節を番号で表し、写した節に振る新しい番号でつながりを返す形にしています。どちらも「元の節から写した節への対応を辞書に持つ」所が同じです
  • 書き終えると「輪があっても止まりますか?」「同じ節を 2 回写していませんか?」と聞かれます

つながりが両向きか、輪があるか、離れたまとまりも写すかを最初に確かめておくと、たどり方が決まります。

ヒント

まだ開いていません

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

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、この型の要点を表とコードでまとめています。

グラフ探索は、つながりを近い順か奥からたどります。迷路、最短の手数、講座を受ける順のような問題が同じ形です。

main.py
def clone_graph(adj: list[list[int]], start: int) -> list[list[int]]:
    """
    Goal: 何を返すか

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

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

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


# 「実行」を押すと、この行の結果が出ます
print(clone_graph([[1, 2], [0, 3], [0, 3], [1, 2]], 3))

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