DFSとBFSの違いは理解していますが、どちらを使用する方が実際的かを知りたいのですが。
誰かがDFSがBFSを打ち負かす方法の例を教えてもらえますか?
DFSとBFSの違いは理解していますが、どちらを使用する方が実際的かを知りたいのですが。
誰かがDFSがBFSを打ち負かす方法の例を教えてもらえますか?
回答:
これは、検索ツリーの構造とソリューション(別名検索対象アイテム)の数と場所に大きく依存します。
ツリーが非常に深く、解決策がまれである場合、深さ優先検索(DFS)には非常に長い時間がかかる可能性がありますが、BFSはより高速になる可能性があります。
ツリーが非常に広い場合、BFSが必要とするメモリーが多すぎる可能性があるため、完全に実用的でない場合があります。
ソリューションが頻繁であるが、ツリーの深い場所にある場合、BFSは非現実的です。
しかし、これらは経験則にすぎません。おそらく実験する必要があるでしょう。
深さ優先検索は、ゲームのシミュレーション(および現実世界のゲームのような状況)でよく使用されます。一般的なゲームでは、いくつかの可能なアクションの1つを選択できます。それぞれの選択肢はさらなる選択肢につながり、それぞれの選択肢はさらなる選択肢につながります。
たとえば、チェスや三目並べなどのゲームでは、何の動きをするかを決めるときに、動きを精神的に想像してから、対戦相手の可能な反応、そして反応などを想像することができます。どの動きが最良の結果につながるかを見て、何をすべきかを決めることができます。
ゲームツリーの一部のパスのみが勝利につながります。相手によって勝つものもあり、そのようなエンディングに到達した場合、前のノードにバックアップまたはバックトラックして、別のパスを試す必要があります。このようにして、成功の結論が出るパスが見つかるまでツリーを探索します。次に、このパスに沿って最初の移動を行います。
幅優先検索には興味深い特性があります。最初に、始点から1エッジ離れているすべての頂点が検出され、次に2エッジ離れているすべての頂点が検出されます。これは、開始頂点から特定の頂点までの最短経路を見つけようとしている場合に役立ちます。BFSを開始すると、指定された頂点が見つかると、これまでに追跡したパスがノードへの最短パスであることがわかります。短いパスがあった場合、BFSはすでにそれを見つけていただろう。
幅優先検索は、BitTorrent、GPSシステムなどのピアツーピアネットワーク内の近隣ノードの検索、近くの場所の検索、指定した距離にいる人の検索などのソーシャルネットワーキングサイトなどに使用できます。
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を使用して、最短経路を見つけたり、サイクルを検出したり(再帰を使用)することができます。
DFSはBFSよりもスペース効率が良いですが、不必要な深さになる可能性があります。
それらの名前は明らかです:幅が広く(つまり、分岐係数が大きい)、深さが非常に限られている(たとえば、「移動」の数が限られている)場合、BFSよりもDFSの方が優先されます。
DFSのスペース効率を組み合わせたあまり知られていないバリアントがあることを言及しておく必要がありますが、BFSのレベル順の訪問は(累積的に)深さ優先の反復探索です。このアルゴリズムはいくつかのノードを再訪しますが、それは漸近的差異の一定の要因を提供するだけです。
プログラマーとしてこの質問に取り組むとき、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())
これとは対照的に、まだ訪問していないノードのリストの個別のデータ構造を維持する必要がある、幅優先の検索とは対照的です。
BFSの重要な利点の1つは、重み付けされていないグラフで2つのノード間の最短パスを見つけるために使用できることです。一方、DFSを同じに使用することはできません。
BFSの場合、Facebookの例を検討できます。他の友達プロフィールからFBプロフィールから友達を追加する提案を受け取ります。A-> Bで、B-> EとB-> Fがあるとすると、AはEとFの候補を取得します。2番目のレベルまで読み取るには、BFSを使用している必要があります。DFSは、ソースから宛先までのデータに基づいて何かを予測するシナリオに基づいています。すでにチェスや数独について述べたように。ここで私が違うのは、DFSは最初にパス全体をカバーするので、DFSを使用して最短パスを使用する必要があると私は確信しているからです。しかし、BFSは貪欲なアプローチを使用するため、最短パスのように見えるかもしれませんが、最終結果は異なる場合があります。私の理解が間違っているかどうか教えてください。
一部のアルゴリズムは、DFS(またはBFS)の特定のプロパティに依存して機能します。たとえば、2接続コンポーネントを見つけるためのHopcroftおよびTarjanアルゴリズムは、DFSが遭遇したすでにアクセスされた各ノードがルートから現在探索されているノードへのパス上にあるという事実を利用しています。
簡単な言葉で:
幅優先検索(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)を使用できます。
深さ優先検索はノードが処理されるときにスタックを使用するため、DFSではバックトラックが提供されます。幅優先検索では、スタックではなくキューを使用して、処理されるノードを追跡するため、BFSにはバックトラッキングは提供されません。
これは、特定のケースで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,