web-dev-qa-db-ja.com

最短経路を検索するとき、幅優先検索はどのように機能しますか?

私はいくつかの研究を行ってきましたが、このアルゴリズムの一部が欠けているようです。幅優先検索がどのように機能するかは理解していますが、個々のノードがどこに行くことができるかを単に伝えるのではなく、特定のパスにどのように到達するかを正確に理解していません。私の混乱を説明する最も簡単な方法は、例を提供することだと思います。

たとえば、次のようなグラフがあるとします。

enter image description here

そして私の目標は、AからEに到達することです(すべてのエッジに重みがありません)。

それが私の起源だからです。 Aをキューに入れ、その後すぐにAをデキューして探索します。 AはBとDに接続されているため、これによりBとDが生成されます。したがって、BとDの両方をキューに入れます。

Bをデキューし、それを探索し、A(既に探索済み)、Cにつながることを見つけたので、Cをキューに入れます。次にDをデキューし、それが私の目標であるEにつながることを見つけます。次に、Cをデキューし、それが目的のEにもつながることを確認します。

論理的には最速のパスがA-> D-> Eであることは知っていますが、幅優先検索がどのように役立つかわかりません-終了時に結果を分析して確認できるようにパスを記録する方法最短経路はA-> D-> Eですか?

また、実際にはツリーを使用していないため、「親」ノードはなく、子ノードのみが存在することに注意してください。

111
Jake

技術的には、BFSはそれ自体では最短パスを見つけることができません。単にBFSが最短パスを探していないからです。BFSはグラフを検索するための戦略を説明しますが、特に何でも。

Dijkstraのアルゴリズム は、BFSを適応させて、単一ソースの最短パスを見つけられるようにします。

Originからノードまでの最短経路を取得するには、グラフ内の各ノードに対して2つの項目を維持する必要があります。現在の最短距離と最短経路の前のノードです。最初はすべての距離が無限に設定され、すべての距離が空に設定されます。この例では、Aの距離をゼロに設定してから、BFSに進みます。各ステップで、子孫の距離を改善できるかどうかを確認します。つまり、Originから先行ノードまでの距離に、探索しているEdgeの長さを加えた値が、問題のノードの現在の最適距離よりも小さいことを確認します。距離を改善できる場合は、新しい最短パスを設定し、そのパスが取得された先祖を思い出してください。 BFSキューが空になったら、ノード(この例ではE)を選択し、その前身をトラバースしてOriginに戻します。これにより、最短パスが得られます。

これが少し分かりにくいように思える場合、ウィキペディアにはトピックに関するNice pseudocode section があります。

71
dasblinkenlight

上記で指摘したように、BFSはonlyを使用して、グラフ内の最短パスを見つけることができます。

  1. ループはありません

  2. すべてのエッジのウェイトは同じか、ウェイトがありません。

最短パスを見つけるには、ソースから開始して、幅優先検索を実行し、宛先ノードが見つかったら停止するだけです。あなたがする必要がある唯一の追加事項は、訪問したすべてのノードの前のノードを保存する配列previous [n]を持つことです。 sourceの以前のものはnullにできます。

パスを出力するには、ソースからprevious []配列を単純にループして、宛先に到達してノードを出力します。 DFSを使用して、同様の条件下でグラフの最短パスを見つけることもできます。

ただし、グラフがより複雑で、重み付きのエッジとループを含む場合、BFSのより洗練されたバージョン、つまりダイクストラのアルゴリズムが必要です。

43
javaProgrammer

チュートリアルはこちら から

「グラフ内のすべてのエッジに重みが付けられていない(または同じ重みである)場合、ノードに最初にアクセスしたときにソースノードからそのノードへの最短パスになるという非常に便利なプロパティがあります」

20
acheron55

私は3日を無駄にしました
グラフの質問を最終的に解決しました
のために使用される
最短距離を見つける
BFSを使用

経験を共有したい。

When the (undirected for me) graph has
fixed distance (1, 6, etc.) for edges

#1
We can use BFS to find shortest path simply by traversing it
then, if required, multiply with fixed distance (1, 6, etc.)

#2
As noted above
with BFS
the very 1st time an adjacent node is reached, it is shortest path

#3
It does not matter what queue you use
   deque/queue(c++) or
   your own queue implementation (in c language)
   A circular queue is unnecessary

#4
Number of elements required for queue is N+1 at most, which I used
(dint check if N works)
here, N is V, number of vertices.

#5
Wikipedia BFS will work, and is sufficient.
    https://en.wikipedia.org/wiki/Breadth-first_search#Pseudocode

上記のすべての選択肢を試し、上記の検証と再検証を何度も繰り返して3日間失った
これらは問題ではありません。
(上記の5で問題を見つけた場合は、他の問題を探すために時間を費やしてください)。


詳細な説明以下のコメントから:

      A
     /  \
  B       C
 /\       /\
D  E     F  G

上記があなたのグラフであると仮定します
グラフは下向きになります
Aの場合、隣接するのはBとCです
Bの場合、隣接するのはDとEです
Cの場合、隣接するのはFとGです

たとえば、開始ノードはAです

  1. a、B、Cに到達すると、AからB&Cへの最短距離は1です。

  2. bを介してDまたはEに到達すると、A&Dへの最短距離は2です(A-> B-> D)

同様に、A-> Eは2です(A-> B-> E)

また、A-> F&A-> Gは2

したがって、ノード間の距離が1ではなく、6の場合は、答えに6を掛けるだけです
例、
それぞれの距離が1の場合、A-> Eは2です(A-> B-> E = 1 + 1)
それぞれの距離が6の場合、A-> Eは12です(A-> B-> E = 6 + 6)

はい、bfsは任意のパスを取ることができます
ただし、すべてのパスについて計算しています

aからZに移動する必要がある場合は、Aから中間Iまでのすべてのパスを移動します。多くのパスがあるため、Iまでの最短パス以外はすべて破棄し、次のノードJまでの最短パスを続行します。
再び、IからJへのパスが複数ある場合、最短のパスのみを使用します
例、
仮定、
A->私は距離5を持っています
(STEP)は、I-> Jと仮定します。7が最短であるため、距離7と8の複数のパスがあります。
A-> Jは5(A-> I最短)+ 8(最短)= 13
したがって、A-> Jは13です。
Zに到達するまで、J-> Kなどについて上記(STEP)を繰り返します。

このパートを2、3回読んで、紙に描いてください。


acheron55 answer に基づいて、可能な実装を投稿しました here
以下に簡単な要約を示します。

あなたがしなければならないのは、目標に到達したパスを追跡することです。それを行う簡単な方法は、ノード自体ではなく、ノードに到達するために使用されるパス全体をQueueにプッシュすることです。
そうすることの利点は、ターゲットに到達すると、キューがその到達に使用されるパスを保持することです。
これは、ノードが複数の親を持つことができる循環グラフにも適用できます。

1
c0der