双方向BFSパターン

上級

カテゴリ: graphs

概要

双方向BFS(Bidirectional Breadth-First Search)は、グラフの始点と終点の両方から同時にBFSを実行し、二つの探索が交わる点を見つけることで最短経路を効率的に特定するアルゴリズムです。通常のBFSが始点から探索空間を拡大するのに対し、双方向BFSは始点と終点の両方から探索を行い、二つの探索フロンティアが交わった時点で最短経路が見つかります。これにより、探索空間を大幅に削減でき、特に大規模なグラフや状態空間で効果を発揮します。理論的には、単方向BFSの時間複雑度がO(b^d)(bは分岐係数、dは深さ)であるのに対し、双方向BFSはO(b^(d/2))となり、指数関数的な改善が得られます。

コードテンプレート

Java
Python
- 双方向BFSによる最短経路探索
public int bidirectionalBFS(Graph graph, int start, int end) {
    if (start == end) return 0;
    
    // 両方向からの訪問セットとキュー
    Set<Integer> visitedForward = new HashSet<>();
    Set<Integer> visitedBackward = new HashSet<>();
    Queue<Integer> queueForward = new LinkedList<>();
    Queue<Integer> queueBackward = new LinkedList<>();
    Map<Integer, Integer> distanceForward = new HashMap<>();
    Map<Integer, Integer> distanceBackward = new HashMap<>();
    
    // 初期化
    visitedForward.add(start);
    visitedBackward.add(end);
    queueForward.offer(start);
    queueBackward.offer(end);
    distanceForward.put(start, 0);
    distanceBackward.put(end, 0);
    
    // 両方向からBFSを実行
    while (!queueForward.isEmpty() && !queueBackward.isEmpty()) {
        // サイズが小さい方のキューから探索を進める
        if (queueForward.size() <= queueBackward.size()) {
            int shortestPath = expandFrontier(graph, queueForward, visitedForward, visitedBackward, distanceForward, distanceBackward);
            if (shortestPath != -1) return shortestPath;
        } else {
            int shortestPath = expandFrontier(graph, queueBackward, visitedBackward, visitedForward, distanceBackward, distanceForward);
            if (shortestPath != -1) return shortestPath;
        }
    }
    
    return -1; // 経路が見つからない場合
}

private int expandFrontier(Graph graph, Queue<Integer> queue, Set<Integer> visited, Set<Integer> visitedOpposite,
                          Map<Integer, Integer> distance, Map<Integer, Integer> distanceOpposite) {
    int node = queue.poll();
    int currentDistance = distance.get(node);
    
    for (int neighbor : graph.getNeighbors(node)) {
        if (!visited.contains(neighbor)) {
            visited.add(neighbor);
            queue.offer(neighbor);
            distance.put(neighbor, currentDistance + 1);
            
            // 反対側の探索と交わるかチェック
            if (visitedOpposite.contains(neighbor)) {
                return currentDistance + 1 + distanceOpposite.get(neighbor);
            }
        }
    }
    
    return -1; // この拡張では交差点が見つからなかった
}
Java

ひらめきのサポート

問題文に以下のようなキーワードや特徴がある場合、このパターンの適用を検討してみましょう。

双方向BFS幅優先探索最短経路グラフ探索状態空間交差点フロンティア両方向効率的な探索経路探索始点と終点探索空間の削減単語変換迷路状態遷移

チャート式ひらめきポイント

問題を読んだときに、「ソート済み配列で何かを見つける」「二つの要素の関係を調べる」「効率的に配列を操作する」などの特徴があれば、 双指針パターンが適用できる可能性が高いです。特に、O(n²)の計算量を避けたい場合に有効です。

このパターンを使用する問題例

  • 1

    単語変換問題(Word Ladder): 一度に1文字だけ変更して、ある単語から別の単語への最短変換経路を見つける

  • 2

    スライディングパズル(Sliding Puzzle): 最小の移動回数でパズルを解く

  • 3

    ナイトの最短経路(Knight's Shortest Path): チェス盤上のナイトが始点から終点へ移動する最短経路を見つける

  • 4

    迷路の最短経路(Shortest Path in Maze): 迷路内の始点から終点への最短経路を見つける

  • 5

    最小遺伝子変異(Minimum Genetic Mutation): 一度に1文字だけ変更して、ある遺伝子配列から別の配列への最短変換経路を見つける

関連するパターン

bfsdfsshortest-patha-star-algorithm