Menu
Coddy logo textTech

幅優先探索 (BFS)

最終更新

幅優先探索はグラフをレベルごとに探索します。始点ノードから出発し、まずその直接の隣接ノードすべてを訪問し、次にそれらの未訪問の隣接ノードすべてを訪問し、というように進み - 距離が増していく輪の形で外側へ広がります。上の再生を押すと、始点ノードから一層ずつ広がっていく様子を見られます。

BFS は先入れ先出しのキューを使い、これがレベルごとの順序を強制します。ホップ距離の順にノードへ到達するため、BFS は重みなしグラフで最短経路(辺の数が最小)を見つけます。各ノードと辺を一度ずつ訪問するので、O(V + E) 時間で動作します。

時間計算量と空間計算量

指標計算量備考
時間O(V + E)各頂点と辺を一度ずつ訪問
空間O(V)キューと訪問済み集合、最悪の場合すべてのノード
探索順レベルごと最も近いノードから、輪の形で
最短経路はい(重みなし)各ノードへ最小の辺数で到達

ステップごと

ステップ何が起こるか
1始点ノードをキューに入れる。
2先頭のノードを取り出し、訪問済みとして印を付ける。
3その隣接ノードそれぞれを調べる。
4まだ訪問済みでもキューにもない隣接ノードをキューに入れる。
5キューが空になるまで繰り返す。

具体例

ノード 0 からこのグラフを探索する。ここで 0-10-21-32-32-4 は辺である:

ステップ訪問済みキュー(フロンティア)
開始{}[0]
0 を取り出す{0}[1, 2]
1 を取り出す{0, 1}[2, 3]
2 を取り出す{0, 1, 2}[3, 4]
3 を取り出す{0, 1, 2, 3}[4]
4 を取り出す{0, 1, 2, 3, 4}[](完了)

BFS を使うべきとき

使うべきとき避けるべきとき
重みなしグラフで最短経路が必要なとき辺に重みがあるとき - 代わりに Dijkstra を使う
目標がおそらく始点の近くにあるときグラフが非常に幅広いとき - キューが巨大なフロンティアを保持しうる
グラフをレベル順に探索したいとき任意のノードに到達できればよいだけのとき、DFS のほうがメモリを節約できる
連結成分や最小ホップ経路を求めるときトポロジカルソートや閉路検出が必要なとき - DFS のほうが適する

Breadth-First Searchのコード

Python, JavaScript, Java, C++, Cによるクリーンで実行可能なBreadth-First Searchの実装です。言語を選んでコードをコピーするか、Coddyプレイグラウンドに読み込んだ状態で開けます。

PythonでのBreadth-First Searchのコード

Python
1from collections import deque2
3
4def bfs(graph, start):5    visited = {start}6    queue = deque([start])7    order = []8    while queue:9        node = queue.popleft()10        order.append(node)11        for neighbor in graph[node]:12            if neighbor not in visited:13                visited.add(neighbor)  # mark on enqueue, not dequeue14                queue.append(neighbor)15    return order16
17
18graph = {19    "A": ["B", "C"],20    "B": ["D", "E"],21    "C": ["F"],22    "D": [],23    "E": ["F"],24    "F": [],25}26
27print("BFS order:", " -> ".join(bfs(graph, "A")))
このコードをPythonプレイグラウンドで実行

幅優先探索に関するよくある質問

BFS の時間計算量は?
BFS は O(V + E) 時間で動作します。ここで V は頂点の数、E は辺の数で、各頂点を一度訪問し各辺を一度調べるためです。キューと訪問済み集合のために O(V) の空間を使います。
BFS は最短経路を見つけますか?
はい、重みなしグラフでは見つけます。BFS は始点からのホップ距離の順にノードへ到達するため、あるノードに最初に到達するのは辺の数が最小の経路を通ってです。重み付きグラフでは代わりに Dijkstra のアルゴリズムが必要です。
BFS と DFS の違いは?
BFS はキューを使ってレベルごとに(近いものから)探索し、DFS はスタックを使って一つの枝を深く進んでから戻ります。BFS は重みなしの最短経路を見つけ、DFS は幅広いグラフでメモリを節約でき、閉路検出やトポロジカルソートに適します。
Dijkstra のアルゴリズムではなく BFS を使うべきなのはいつですか?
すべての辺のコストが等しいときは BFS を使いましょう。優先度付きキューなしで O(V + E) 時間で辺の数が最小の経路を見つけるからです。辺の重みが異なるときは Dijkstra のアルゴリズムが必要で、重み付きグラフに素の BFS を実行すると、最小コストではなく最小ホップの経路が得られます。
なぜ BFS には訪問済み集合が必要なのですか?
グラフには閉路が含まれることがあるため、訪問済み集合がないと BFS は同じノードを何度もキューに入れて無限にループします。ノードを最初にキューに入れるとき(取り出すときではなく)に印を付けることで、同じノードが異なる隣接ノードによって二度キューに追加されるのも防げます。
ノードはキューに入れるときと取り出すときのどちらで訪問済みにすべきですか?
キューに入れるときに印を付けます。取り出すまで待つと、処理される前に同じノードが異なる隣接ノードによって複数回キューに追加され、メモリと時間を浪費します。キューに入れるときに印を付ければ、各ノードがちょうど一度だけキューに入ることが保証されます。
Coddy programming languages illustration

Coddy でアルゴリズムをマスターしよう

始める