web-dev-qa-db-ja.com

Depth-First Search(DFS)とBreadth-First Search(BFS)の併用が実際的な場合はいつですか?

私はDFSとBFSの違いを理解していますが、どちらを使用する方が実用的なのかを知りたいですか。

DFSがどのようにBFSに勝るのか、またその逆の例を誰かに教えてください。

284
Parth

これは、検索ツリーの構造とソリューションの数と場所(別名検索アイテム)によって大きく異なります。

  • もし解が木の根から遠くないとわかっているなら、幅優先探索(BFS)がより良いかもしれません。
  • ツリーが非常に深く、解決策が稀である場合は、深さ優先探索(DFS)に非常に長い時間がかかるかもしれませんが、BFSはもっと速いかもしれません。

  • ツリーが非常に広い場合、BFSには多すぎるメモリが必要になる可能性があるため、まったく実用的ではない可能性があります。

  • 解決策が頻繁ではあるがツリーの奥深くにある場合、BFSは実用的ではない可能性があります。

  • 検索ツリーが非常に深い場合は、深さ優先検索(DFS)の検索深さを制限する必要があります(たとえば、反復深化など)。

しかし、これらは経験則にすぎません。あなたはおそらく実験する必要があるでしょう。

282

深さ優先探索

深さ優先探索は、ゲームのシミュレーション(および現実の世界ではゲームのような状況)でよく使用されます。典型的なゲームでは、いくつかの可能な行動のうちの1つを選ぶことができます。それぞれの選択はさらなる選択へと導き、それぞれの選択はさらなる選択へと導きます。

enter image description here

たとえば、チェス、三目並べなどのゲームでは、どのような行動をとるかを決めるとき、精神的に行動を想像し、次に相手の反応、次に反応などを想像することができます。どの動きが最良の結果につながるのかを見ることで、何をするべきかを決めることができます。

ゲームツリー内のいくつかのパスだけがあなたの勝利につながります。あなたがそのような結末に到達したとき、あなたは前のノードにバックアップするか、バックトラックしなければならず、別の道を試す必要があります。このようにして、成功した結論を持つパスが見つかるまでツリーを探索します。それからあなたはこの道に沿って最初の動きをします。


幅優先検索

幅優先探索には興味深い特性があります。最初に始点から1 Edge離れたすべての頂点を見つけ、次に2 Edge離れたすべての頂点を見つけるという具合です。開始頂点から指定頂点までの最短パスを見つけようとしている場合、これは便利です。 BFSを開始して、指定された頂点を見つけたら、それまでトレースしたパスがそのノードへの最短パスであることがわかります。もっと短いパスがあれば、BFSはすでにそれを見つけているはずです。

幅優先探索は、BitTorrentのようなピアツーピアネットワーク内の近隣ノード、近くの場所を見つけるためのGPSシステム、指定された距離内の人々を見つけるためのソーシャルネットワーキングサイトなどを見つけるために使用することができます。

106

---からの素晴らしい説明 http://www.programmerinterview.com/index.php/data-structures/dfs-vs-bfs/

BFSの例

これはBFSがどのように見えるかの例です。これは、ITERATIVEアプローチでQUEUEを使用するレベルオーダーツリートラバーサルのようなものです(ほとんどの場合RECURSIONはDFSになります)。番号は、BFSでノードがアクセスされる順序を表します。

enter image description here

深さ優先探索では、ルートから始めて、探しているノードが見つかるまで、またはリーフノード(子のないノード)に到達するまで、可能な限りツリーのブランチの1つをたどります。あなたが葉の節に当たったならば、あなたは未知の子供たちと最も近い祖先で検索を続けます。

DFSの例

これはDFSがどのように見えるかの例です。バイナリツリーでのポストオーダートラバースは、最初にLeafレベルから作業を開始すると思います。番号は、DFS内でノードがアクセスされる順序を表します。

enter image description here

DFSとBFSの違い

BFSとDFSを比較すると、DFSの大きな利点は、各レベルですべての子ポインタを格納する必要がないため、BFSよりもメモリ要件がはるかに少ないことです。データと探しているものに応じて、DFSまたはBFSのいずれかが有利になる可能性があります。

たとえば、家系図で、まだ生きている木の上で誰かを探している場合、その人が木の下にいると仮定しても安全です。これは、BFSがその最後のレベルに達するのに非常に長い時間がかかることを意味します。しかし、DFSは目標を早く見つけるでしょう。しかし、非常に昔に亡くなった家族を探しているのであれば、その人は木のてっぺんに近づくでしょう。その場合、BFSは通常DFSよりも高速です。そのため、どちらの利点も、データと探しているものによって異なります。

もう1つの例はFacebookです。友達の友達への提案。私たちはBFSを使うことができる提案をするためにすぐに友達が必要です。最短経路を見つけるか、DFSを使用できるサイクルを(再帰を使用して)検出することができます。

100

幅優先検索は、ツリーの深さが変わる可能性がある場合に一般に最善の方法であり、ソリューションのためにツリーの一部を検索するだけで済みます。たとえば、開始値から最終値までの最短パスを見つけることは、BFSを使用するのに適した場所です。

Depth First Searchは、ツリー全体を検索する必要がある場合によく使用されます。 BFSより(再帰を使用して)実装する方が簡単で、必要な状態も少なくて済みます。BFSでは「フロンティア」全体を格納する必要があるのに対し、DFSでは現在の要素の親ノードのリストを格納するだけです。

33
Nick Johnson

DFSはBFSよりもスペース効率に優れていますが、不要な深さになる可能性があります。

彼らの名前は明らかにしています:大きな幅(すなわち大きな分岐係数)があるが非常に限られた深さ(例えば限られた数の「移動」)があるならば、DFSはBFSよりも好まれます。


IDDFSについて

DFSのスペース効率を組み合わせた、あまり知られていない変種がありますが、(累積的に)BFSのレベル順の訪問が 反復深さ優先の深さ優先検索 です。このアルゴリズムはいくつかのノードを再検討します、しかしそれは漸近的な違いの一定の要因に貢献するだけです。

23

プログラマーとしてこの質問に取り組むとき、1つの要因が際立っています:あなたが再帰を使っているならば、追加のデータ構造を維持する必要がないので、深さ優先探索は---実装するためにより簡単ですまだ探索していないノードを含みます。

ノードに「すでに訪問した」情報を格納している場合は、無指向グラフを縦型検索します。

def dfs(Origin):                               # DFS from Origin:
    Origin.visited = True                      # Mark the Origin as visited
    for neighbor in Origin.neighbors:          # Loop over the neighbors
        if not neighbor.visited: dfs(next)     # Visit each neighbor if not already visited

「すでに訪れた」情報を別のデータ構造に格納する場合:

def dfs(node, visited):                        # DFS from Origin, with already-visited set:
    visited.add(node)                          # Mark the Origin as visited
    for neighbor in node.neighbors:            # Loop over the neighbors
        if not neighbor in visited:            # If the neighbor hasn't been visited yet,
            dfs(node, visited)                 # then visit the neighbor
dfs(Origin, set())

これとは対照的に、幅優先検索では、まだ訪問していないノードのリスト用に別のデータ構造を維持する必要があります。

12
Gilles

BFSの重要な利点の1つは、重み付けされていないグラフ内の任意の2つのノード間の最短パスを見つけるために使用できることです。一方、 私たちは同じようにDFSを使うことはできません

12

BFSの場合、Facebookの例を検討できます。他の友達プロフィールからFBプロフィールから友達を追加する提案を受け取ります。 A-> Bとし、B-> EとB-> Fとすると、AはEとFの候補になります。彼らはBFSを使用して第2レベルまで読み取る必要があります。 DFSは、送信元から送信先へのデータに基づいて何かを予測したいというシナリオに基づいています。チェスや数独についてすでに述べたように。ここで異なる点が1つあるとすれば、DFSが最初に全体のパスをカバーするので、DFSが最短パスに使用されるべきであると私は思います。しかし、BFSは貪欲なアプローチを採用するので、最短経路のように見えるかもしれませんが、最終的な結果は異なるかもしれません。私の理解が間違っているかどうか私に知らせてください。

6
user2056463

いくつかのアルゴリズムは機能するためにDFS(またはBFS)の特定の特性に依存します。例えば、2連結成分を見つけるためのHopcroftとTarjanのアルゴリズムは、DFSが遭遇した各訪問済みノードがルートから現在探索中のノードへのパス上にあるという事実を利用します。

4
Rafał Dowgird

DFSとBFSの特性によると。たとえば、最短経路を見つけたいときなどです。我々は通常bfsを使います、それは「最短」を保証することができます。しかし、DFSは私たちがこの点から来ることができることを保証することができるだけでその点を達成することができ、「最短」を保証することはできません。

2
yeuyanglou

ツリーの幅が非常に大きく、奥行きが小さい場合は、再帰スタックがオーバーフローしないようにDFSを使用します。幅が狭く奥行きが非常に大きい場合はBFSを使用してツリーをトラバースします。

1
nil96

深さ優先探索はノードの処理時にスタックを使用するため、バックトラッキングはDFSで提供されます。 Breadth-First Searchesは処理されるノードを追跡するためにスタックではなくキューを使用するため、BFSではバックトラックは提供されません。

1

以下はあなたが求めていることに対する包括的な答えです。

簡単な言葉で:

Breadth First Search(BFS)アルゴリズムは、その名前の "Breadth"から、ノードのアウトエッジを介してノードのすべての隣接ノードを検出し、次に、アウトサイドエッジを介して前述の隣接ノードの未訪問の隣接ノードを検出します。元の発信元から到達可能なノードが訪問されます(未訪問のノードが残っている場合など、続けて別の発信元を取得できます)。そのため、エッジの重みが均一であれば、ノード(元のソース)から別のノードへの最短パス(存在する場合)を見つけるために使用できます。

深さ優先検索(DFS)アルゴリズムは、その名前「深さ」から、そのアウトエッジを通して最も最近発見されたノードxの未訪問の近隣者を発見する。ノードxからの未訪問のネイバーがいない場合、アルゴリズムは、元のソースから到達可能なすべてのノードが訪問されるまで、ノードxが発見されたノードの未訪問のネイバーを(アウトエッジで)発見するためにバックトラックする(訪問していないノードなどが残っている場合は、続けて別のソースを取得できます)。

BFSとDFSの両方が不完全になる可能性があります。たとえば、ノードの分岐係数が無限大の場合、またはサポートするリソース(メモリ)にとって非常に大きい場合(たとえば、次に検出されるノードを格納する場合)、検索されたキーが遠くにある場合でもBFSは完了しません。起源からの少数の端の。この無限分岐係数は、発見すべき所与のノードからの無限の選択(隣接ノード)のためであり得る。深さが無限大、またはサポートするリソース(メモリ)にとって非常に大きい場合(たとえば、次に発見されるノードを格納するとき)、検索されたキーが元のソースの3番目の近隣になり得るとしてもDFSは完了しない。この無限の深さは、アルゴリズムが発見するすべてのノードに対して、以前には見られなかった少なくとも新しい選択(隣接ノード)が存在するという状況のためであり得る。

したがって、BFSとDFSをいつ使用するかを決定できます。管理可能な限定分岐係数と管理可能な限定深度を扱っているとします。探索されたノードが浅い、すなわち元のソースからのいくつかのエッジの後に到達可能である場合、それからBFSを使用することがより良い。他方、探索されたノードが深い、すなわち元のソースからの多くのエッジの後に到達可能である場合、DFSを使用することがより良い。

たとえば、ソーシャルネットワークで特定の人と同じような興味を持っている人を検索する場合は、この人からのBFSを元の情報源として適用できます。これらの人の大部分は彼の直接の友人または友人の友人です。または2つのエッジまで。一方、特定の人の興味がまったく異なる人を検索したい場合は、この人からのDFSを元の情報源として適用できます。 ....つまり辺が多すぎます。

BFSとDFSの用途は、それぞれを検索するメカニズムによっても異なります。たとえば、あるノードから別のノードへの到達可能性を確認したい場合は、BFS(分岐係数が管理可能であると仮定)またはDFS(深さが管理可能であると仮定)を使用できます。また、どちらもグラフのトポロジカルソートなどの同じタスクを解決できます(ある場合)。 BFSを使用して、ノード(元のソース)から別のノードへの、単位ウェイトエッジを持つ最短経路を見つけることができます。一方、DFSは、非巡回グラフ内の2つのノード間の最長パスを検出するなど、詳細に進むという性質のために、すべての選択肢を使い果たすために使用できます。またDFSは、グラフ内の周期検出にも使用できます。

結局、無限の深さと無限の分岐係数があれば、反復深化検索(IDS)を使うことができます。

1
Mosab Shaheen

それが使われる状況によります。グラフをトラバースする問題があるときはいつでも、それは何らかの目的のためにそれをします。重み付けされていないグラフで最短経路を見つける問題、またはグラフが2部構造であるかどうかを調べる問題がある場合は、BFSを使用できます。サイクル検出やバックトラックを必要とするロジックの問題では、DFSを使用できます。

0
ankur314

それはあなたがどんな問題に直面しているかによると思います。

  1. 単純グラフ上の最短経路 - > bfs
  2. 考えられるすべての結果 - > dfs
  3. グラフ上で検索(ツリー、マティクスもグラフとして扱う) - > dfs ....
0
BenDan

これは、特定のケースでBFSがDFSより優れていることを示す良い例です。 https://leetcode.com/problems/01-matrix/

正しく実装されると、両方の解決策は現在のセル+1よりも遠くにあるセルを訪問するはずです。しかし、DFSは非効率的で、同じセルを繰り返し訪問した結果、O(n * n)の複雑さになります。

例えば、

1,1,1,1,1,1,1,1, 
1,1,1,1,1,1,1,1, 
1,1,1,1,1,1,1,1, 
0,0,0,0,0,0,0,0,
0
coderek