素集合 (Union-Find) でケーブルでつながったパソコンの組を数える

パソコンの台数と、2 台を結ぶケーブルの並びから、行き来できるパソコンの組 (ネットワーク) がいくつあるかを数える。ケーブルを 1 本ずつ足しながら、別々の組を 1 つに合わせていく素集合の練習です。

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

問題

事務所に n 台のパソコンがあり、0 から n - 1 の番号が付いています。ケーブルの並び cables を受け取ります。cables[i] = [a, b] は、パソコン a と b を直接つなぐ、両向きのケーブルです。ケーブルを何本かたどって行き来できるパソコンどうしは、同じネットワークに入ります。ネットワークがいくつあるかを返す関数を書いてください。

  • 1 本もケーブルがつながっていないパソコンも、それだけで 1 つのネットワークです
  • n は 1 以上 1000 以下で、cables の長さは 0 以上 3000 以下です
  • 自分自身につなぐケーブルはありません。同じ 2 台を結ぶケーブルが 2 本以上あってもかまいません

入力と出力の例

例 1

0、3、5 の 3 台と、1、4 の 2 台が、それぞれつながっています。2 番は 1 台だけで 1 つのネットワークです。

入力

n = 6
cables = [[0, 3], [3, 5],
  [1, 4]]

出力

3

例 2

0、2、4 の 3 台は輪になってつながり、1 と 3 は同じ 2 台を 2 本のケーブルで結んでいます。ケーブルが余っていても、数えるのはネットワークの数です。

入力

n = 5
cables = [[2, 0], [0, 4],
  [4, 2], [1, 3], [3, 1]]

出力

2

例 3

はじめは 2 台ずつの 4 組ですが、あとのケーブルが組どうしをつなぎ、最後は 8 台が 1 つのネットワークになります。

入力

n = 8
cables = [[0, 1], [2, 3],
  [4, 5], [6, 7], [1, 2],
  [5, 6], [3, 4]]

出力

1

図で見る

例 1 で、解き方がどう動くか。押すと大きく開きます。

面接での聞かれ方

面接では、サーバーの回線、友だちのつながり、道でつながった町などの場面で渡されることが多いです。

  • 「台数とつなぎ方の一覧が渡されます。つながっている台のかたまりがいくつあるか数えてください」
  • 書き終えたあとに「深さ優先でも解けますが、素集合にすると何がうれしいですか?」と聞かれます
  • 「ケーブルが 1 本増えるたびに、いまのネットワークの数を答えてください」と、つなぎ足していく形に広げられることもあります
  • 「全部を 1 つのネットワークにするには、あと何本ケーブルが要りますか?」と聞かれることもあります (答えは、ネットワークの数 - 1 本です)

台の番号が 0 から n - 1 で、ケーブルに出てこない台もあることを、書く前に確かめておくと、数え落としを防げます。

ヒント

まだ開いていません

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

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

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

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

main.py
def count_networks(n: int, cables: list[list[int]]) -> int:
    """
    Goal: 何を返すか

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

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

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


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

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