深さ優先検索(DFS)と幅優先検索(BFS)を使用するのはいつ実用的ですか?[閉まっている]


345

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

誰かがDFSがBFSを打ち負かす方法の例を教えてもらえますか?


4
たぶん、DFSとBFSの完全な用語を質問に言及することができます-人々はこれらの省略形を知らないかもしれません。
Hans-PeterStörr2010




BFSとDFSのいくつかのアプリケーションシナリオに言及しているメモ
Yossarian42

回答:


353

これは、検索ツリーの構造とソリューション(別名検索対象アイテム)の数と場所に大きく依存します。

  • ソリューションがツリーのルートからそれほど遠くないことがわかっている場合は、幅優先検索(BFS)の方が良いかもしれません。
  • ツリーが非常に深く、解決策がまれである場合、深さ優先検索(DFS)には非常に長い時間がかかる可能性がありますが、BFSはより高速になる可能性があります。

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

  • ソリューションが頻繁であるが、ツリーの深い場所にある場合、BFSは非現実的です。

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

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


4
AFAIK再帰は通常、反復よりも多くのメモリを必要とします。
Marek Marczak

3
@MarekMarczak言いたいことがよくわかりません。BFSを反復として使用する場合-解空間が簡単に列挙できない場合は、検索ツリーのn番目のレベル全体をメモリに格納してn + 1番目のレベルを列挙する必要がある場合があります。
Hans-PeterStörr

11
@MarekMarczak DFSの反復バージョンはスタックを使用します。再帰と反復はまったく別のトピックです。
クリントデイグー

頭に浮かんだもう1つのケース:BFSは、グラフが「無限」である場合に便利です(必要です)。言うように、すべての方向に無限に伸びるチェス盤。DFSが戻ることはありません。BFSは、条件が満たされている場合に検索対象を見つけることが保証されています。
ThePartyTurtle

157

深さ優先検索

深さ優先検索は、ゲームのシミュレーション(および現実世界のゲームのような状況)でよく使用されます。一般的なゲームでは、いくつかの可能なアクションの1つを選択できます。それぞれの選択肢はさらなる選択肢につながり、それぞれの選択肢はさらなる選択肢につながります。

ここに画像の説明を入力してください

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

ゲームツリーの一部のパスのみが勝利につながります。相手によって勝つものもあり、そのようなエンディングに到達した場合、前のノードにバックアップまたはバックトラックして、別のパスを試す必要があります。このようにして、成功の結論が出るパスが見つかるまでツリーを探索します。次に、このパスに沿って最初の移動を行います。


幅優先検索

幅優先検索には興味深い特性があります。最初に、始点から1エッジ離れているすべての頂点が検出され、次に2エッジ離れているすべての頂点が検出されます。これは、開始頂点から特定の頂点までの最短経路を見つけようとしている場合に役立ちます。BFSを開始すると、指定された頂点が見つかると、これまでに追跡したパスがノードへの最短パスであることがわかります。短いパスがあった場合、BFSはすでにそれを見つけていただろう。

幅優先検索は、BitTorrent、GPSシステムなどのピアツーピアネットワーク内の近隣ノードの検索、近くの場所の検索、指定した距離にいる人の検索などのソーシャルネットワーキングサイトなどに使用できます。


113

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

BFSの例

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

ここに画像の説明を入力してください

深さ優先検索では、ルートから開始し、探しているノードが見つかるか、リーフノード(子のないノード)に到達するまで、ツリーのブランチの1つを可能な限り追跡します。葉ノードにヒットした場合、探索されていない子を持つ最も近い祖先で検索を続行します。

DFSの例

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

ここに画像の説明を入力してください

DFSとBFSの違い

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

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

もう1つの例はFacebookです。フレンズオブフレンズに関する提案。BFSを使用できる場所を提案するには、すぐに友達が必要です。DFSを使用して、最短経路を見つけたり、サイクルを検出したり(再帰を使用)することができます。


「二分木におけるポストオーダートラバーサル」とは何ですか?
カイルデラニー

8
DFSはBFSよりも短いパスを見つけますか?私はそれが逆だと思います。BFSは最短経路を見つけると思います。だよね?
Naveen Gabriel

1
あなたがソースにクレジットを与えていたならば、もっと高く評価したでしょう。比較部分は、Narasimha Karumanchiによる「Javaで簡単にできるデータ構造」からピックアップされています。
learntogrow-growtolearn 2017年

確かに私はそれを更新できますが、ここで誰かに感謝することは期待していません。私のような貧しい技術者を助けたいだけです。
Kanagavelu Sugumar 2017年

1
@KyleDelaneyには、ツリーをトラバースできる3つの注文があります。予約注文、注文注文、注文注文です。これらは、それぞれ接頭辞インフィックスとポストフィックス表記に対応しています。ツリーを下に移動してからバックアップするときに、最初にアクセスしたときにノードを選択した場合は事前注文であり、2回目が順序付けられている場合は、最後に注文した場合です。この方法で実際にツリーをシリアル化でき、使用した順序を覚えている限り、シリアル化からツリーを再構築できます。
デイブ

43

幅優先検索は、ツリーの深さが変化する可能性があり、ソリューションのツリーの一部のみを検索する必要がある場合、一般的に最良のアプローチです。たとえば、開始値から最終値までの最短経路を見つけることは、BFSを使用するのに適した場所です。

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


26

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

それらの名前は明らかです:幅が広く(つまり、分岐係数が大きい)、深さが非常に限られている(たとえば、「移動」の数が限られている)場合、BFSよりもDFSの方が優先されます。


IDDFS

DFSのスペース効率を組み合わせたあまり知られていないバリアントがあることを言及しておく必要がありますが、BFSのレベル順の訪問は(累積的に)深さ優先反復探索です。このアルゴリズムはいくつかのノードを再訪しますが、それは漸近的差異の一定の要因を提供するだけです。


1
必ずしもスペース効率が良いとは限りません。たとえば、パスグラフを検討してください。
RB

16

プログラマーとしてこの質問に取り組むとき、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())

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



5

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


今私のコメントは少し遅れています。ただし、最短経路を見つけるには、BFSを使用する必要があります。しかし、「DFSは、ソースから宛先までのデータに基づいて何かを予測したいというシナリオに基づいています」というのはすばらしいことです。賞賛!!
Oskarzito

4

一部のアルゴリズムは、DFS(またはBFS)の特定のプロパティに依存して機能します。たとえば、2接続コンポーネントを見つけるためのHopcroftおよびTarjanアルゴリズムは、DFSが遭遇したすでにアクセスされた各ノードがルートから現在探索されているノードへのパス上にあるという事実を利用しています。


4

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

簡単な言葉で:

幅優先検索(BFS)アルゴリズムは、その名前「幅」から、ノードの外端を介してノードのすべての隣接ノードを検出し、次に、前に述べた隣接ノードの未訪問の隣接ノードをそれらの外端を介して検出します。元のソースから到達可能なノードが訪問されます(未訪問のノードが残っている場合などは、続行して別の元のソースを取得できます)。これが、エッジの重みが均一である場合に、ノード(元のソース)から別のノードへの最短パス(存在する場合)を見つけるために使用できる理由です。

Depth First Search(DFS)アルゴリズムは、その名前「Depth」から、outエッジを介して、最後に発見されたノードxの未訪問の隣接ノードを発見します。ノードxから未訪問のネイバーがない場合、アルゴリズムはバックトラックして、ノードxが発見されたノードの(outエッジを介して)未訪問のネイバーを発見します。これは、元のソースから到達可能なすべてのノードが訪問されるまで続きます。 (未訪問のノードが残っている場合などは、続行して別の元のソースを取得できます)。

BFSとDFSはどちらも不完全な場合があります。たとえば、ノードの分岐係数が無限であるか、リソース(メモリ)がサポートするのに非常に大きい場合(たとえば、次に検出されるノードを格納する場合)、検索されたキーが離れていてもBFSは完了しません元のソースからのいくつかのエッジの。この無限分岐要因は、特定のノードから検出する無限の選択肢(隣接ノード)が原因である可能性があります。深さが無限であるか、リソース(メモリ)がサポートするのに非常に大きい場合(たとえば、次に検出されるノードを格納する場合)、検索されたキーが元のソースの3番目のネイバーである可能性があっても、DFSは完全ではありません。この無限の深さは、アルゴリズムが発見するすべてのノードについて、少なくとも以前に訪問されていない新しい選択肢(隣接ノード)が存在する状況が原因である可能性があります。

したがって、BFSとDFSをいつ使用するかを結論付けることができます。管理可能な限られた分岐因子と管理可能な限られた深さを扱っていると仮定します。検索されたノードが浅い、つまり元のソースからのいくつかのエッジの後に到達可能な場合は、BFSを使用することをお勧めします。一方、検索されたノードが深い場合、つまり元のソースから多くのエッジの後に到達可能な場合は、DFSを使用することをお勧めします。

たとえば、ソーシャルネットワークで特定の人物の同様の関心を持つ人々を検索する場合、この人物のBFSを元のソースとして適用できます。これは、これらの人々のほとんどが直接の友人または友人の友人、つまり1人の友人になるためです。または遠方の2つのエッジ。一方、特定の人の関心がまったく異なる人を検索する場合、この人のDFSを元のソースとして適用できます。これは、これらの人のほとんどが彼から非常に離れているためです。 ....つまり、エッジが多すぎます。

BFSとDFSのアプリケーションは、それぞれを検索するメカニズムのために異なる場合があります。たとえば、あるノードから別のノードへの到達可能性を確認したいが、そのノードの位置に関する情報がない場合は、BFS(分岐因子が管理可能であると想定)またはDFS(深度が管理可能であると想定)のいずれかを使用できます。また、どちらもグラフのトポロジカルソートなどの同じタスクを解決できます(ある場合)。BFSを使用すると、ノード(元のソース)から別のノードへの最短パスを、単位重みエッジで見つけることができます。一方、DFSは、非巡回グラフの2つのノード間の最長パスを検出するなど、詳細に進む性質により、すべての選択肢を使い果たすことができます。また、DFSは、グラフのサイクル検出に使用できます。

結局、無限の深さと無限の分岐係数がある場合、反復深化検索(IDS)を使用できます。


2

DFSとBFSの特性によると。たとえば、最短経路を見つけたい場合などです。通常はbfsを使用しますが、「最短」を保証できます。しかし、dfsは、このポイントから到達できることを保証できるだけで、そのポイントを達成でき、「最短」を保証できません。


2

それはあなたが直面している問題に依存すると思います。

  1. 単純なグラフの最短経路-> bfs
  2. すべての可能な結果-> dfs
  3. グラフで検索(ツリー、martixもグラフとして扱う)-> dfs ....

リストの前に空の行を追加すると、答えははるかに良くなります。
montonero

1

深さ優先検索はノードが処理されるときにスタックを使用するため、DFSではバックトラックが提供されます。幅優先検索では、スタックではなくキューを使用して、処理されるノードを追跡するため、BFSにはバックトラッキングは提供されません。


1

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


0

これは、特定のケースで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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.