web-dev-qa-db-ja.com

重み付けされていない無向グラフの2つのノード間のすべての最短パスを見つける

重みのない無向グラフで2つのノード間のすべての最短経路を見つけるのに助けが必要です。

BFSを使用して最短経路の1つを見つけることはできますが、これまでのところ、それらをすべて見つけて印刷する方法については迷っています。

私が使用できるアルゴリズム/擬似コードのアイデアはありますか?

31
user1946334

警告として、グラフ内の2つのノード間に指数関数的に多くの最短パスが存在する可能性があることに注意してください。このためのアルゴリズムには、指数関数的な時間がかかる可能性があります。

そうは言っても、BFSには比較的簡単な変更があり、すべての可能なパスの生成を高速化する前処理ステップとして使用できます。 BFSが実行されると、「レイヤー」で外側に進み、距離0、次に距離1、次に距離2などのすべてのノードへの単一の最短パスを取得することに注意してください。開始ノードからの距離は、開始ノードから距離kのノードにエッジで接続する必要があります。 BFSは、距離kのノードへの長さkのパスを見つけて、エッジによってそれを延長することにより、距離k + 1でこのノードを検出します。

目的がall最短パスを見つけることである場合、距離kのノードへのeveryパスを、距離k + 1のすべてのノードに拡張することにより、BFSを変更できます。単一のEdgeを選択するのではなく、接続します。これを行うには、次の方法でBFSを変更します。処理キューにエンドポイントを追加してEdgeを処理するときは、すぐにそのノードに完了マークを付けないでください。代わりに、そのノードを取得するためにたどったEdgeの注釈が付いたキューにそのノードを挿入します。これにより、リンクするノードが複数ある場合に、同じノードをキューに複数回挿入できる可能性があります。キューからノードを削除する場合、完了としてマークし、キューに再び挿入することはありません。同様に、単一の親ポインターを保存するのではなく、そのノードにリンクした各ノードに1つずつ、複数の親ポインターを保存します。

この変更されたBFSを実行すると、すべてのノードが開始ノードになり、出力エッジがないか、開始ノードから距離k + 1にあり、各ノードへのポインターを持つDAGになります接続されている距離k。そこから、選択したノードからDAG内の開始ノードに戻るすべての可能なパスをリストすることにより、あるノードから開始ノードまでのすべての最短パスを再構築できます。これは再帰的に実行できます。

  • 開始ノードからそれ自体へのパスは、空のパスのみです。
  • 他のノードの場合、各出力エッジをたどってパスを見つけることができ、それらのパスを再帰的に拡張して開始ノードに戻るパスを生成します。

お役に立てれば!

29
templatetypedef

@templatetypedefは正しいのですが、親リンクがノードに追加される前に行わなければならない距離チェックについて言及するのを忘れていました。これは、各ノードでソースからの距離を保持し、子の距離を1ずつ増やすことを意味します。子がすでに訪問されており、距離が短い場合、この増分と親の追加をスキップする必要があります。

public void addParent(Node n) {
    // forbidding the parent it its level is equal to ours
    if (n.level == level) {
        return;
    }
    parents.add(n);

    level = n.level + 1;
}

完全なJava実装は、次のリンクで見つけることができます。

http://ideone.com/UluCBb

5
bitec

これを解決中に同様の問題が発生しました https://oj.leetcode.com/problems/Word-ladder-ii/

私が対処しようとした方法は、最初にBFSを使用して最短距離を見つけることです。最短距離はdとしましょう。ここでDFSを適用し、DFS再帰呼び出しで再帰レベルdを超えないでください。

しかし、これは@templatetypedefで言及されているようにすべてのパスを探索することになります。

2
rgaut

まず、幅優先探索を使用して、すべてのノードの開始までの距離を見つけます。

(ノードが多数ある場合は、A *を使用して、キューの先頭にdistance-to-start > distance-to-start(end-node)があるときに停止できます。これにより、最短パスに属するすべてのノードが提供されます)

次に、エンドノードからバックトラックします。開始までの距離が短い2つ(またはそれ以上)のノードにノードが接続されると、2つ(またはそれ以上)のパスに分岐します。

ステップ1:BFSによってソースからグラフを走査し、各ノードにソースからの最小距離を割り当てます

ステップ2:ターゲットノードに割り当てられた距離は最短の長さです

ステップ3:ソースから、ターゲットノードに到達するか最短の長さに達するまで、最小距離が1つずつ増加するすべてのパスに沿ってDFS検索を実行します。ターゲットノードに到達するたびにパスを出力します。

0
Joe C

templatetypedefあなたの答えは非常に良かったです、そのためにたくさんありがとう(!!)、しかしそれは1つの点を逃しました:

このようなグラフがある場合:

 A-B-C-E-F 
 | | 
 D ------ 

今、私はこのパスが欲しいと想像してみましょう:

A-> E.

次のように展開されます。

 A-> B-> D-> C-> F-> E.

問題は、FをEの親として持つことですが、

 A-> B-> D-> F-E 
 A-> B-> C-> E。
0
Firespy