Swift教室 Swift と競技プログラミングの教室

19. Number of Connected Components in an Undirected Graph

読了目安 約2分

Graph / LeetCode 323 (Medium) — 隣接リストと探索で連結成分を数える。Premium 限定。

この章の目次

問題

LeetCode 323. Number of Connected Components in an Undirected Graph(Medium / カテゴリ: Graph)

LeetCode Premium 限定です。問題の要点は次の要約の通りです。

0 から n - 1 の番号がついた n 個の頂点と、無向辺のリスト edges が与えられます。 edges の各要素 [a, b] は、頂点 a と頂点 b を双方向につなぐ辺です。 辺をたどって行き来できる頂点のまとまりを連結成分と呼びます。 グラフ全体の連結成分の個数を返します。

例を挙げます。 n = 5, edges = [[0, 1], [1, 2], [3, 4]] なら、{0, 1, 2} と {3, 4} の 2 つに分かれるので答えは 2 です。 辺が 1 本もなければ全頂点が孤立し、答えは n です。

無料で読める類題として LeetCode 547. Number of Provinces があります。 入力が隣接行列に変わるだけで、数えるものは同じです。

考え方

ヒント 1

Number of Islands の島数えと同じ構図です。 未訪問の頂点を見つけるたびに成分を 1 つ数え、そこから届く頂点をすべて訪問済みにします。

ヒント 2

まず辺のリストから、頂点ごとの隣の一覧(隣接リスト)を作ります。 訪問済みを覚える配列を持ち、未訪問の頂点を始点に深さ優先探索(DFS)か幅優先探索(BFS)で到達できる頂点を塗りつぶします。 Union-Find でも解けますが、探索で十分です。

Swift 実装のポイント

  • 隣接リストは var adj: [[Int]] = Array(repeating: [], count: n) で作り、辺ごとに両方向へ追加します。
  • スタックで書く DFS は while let v = stack.popLast() が定番です。再帰でも書けます。
  • 始点の列挙は for start in 0..<n where !visited[start] と書けます。
模範解答
Swift
class Solution {
    func countComponents(_ n: Int, _ edges: [[Int]]) -> Int {
        var adj: [[Int]] = Array(repeating: [], count: n)
        for edge in edges {
            adj[edge[0]].append(edge[1])
            adj[edge[1]].append(edge[0])
        }
        var visited = Array(repeating: false, count: n)
        var count = 0
        for start in 0..<n where !visited[start] {
            count += 1
            var stack = [start]
            visited[start] = true
            while let v = stack.popLast() {
                for next in adj[v] where !visited[next] {
                    visited[next] = true
                    stack.append(next)
                }
            }
        }
        return count
    }
}

計算量: O(n + e)(e は辺の数。各頂点と各辺を定数回ずつ処理します)。