Wann ist es sinnvoll, die Tiefensuche (DFS) im Vergleich zur Breitensuche (BFS) zu verwenden? [geschlossen]


345

Ich verstehe die Unterschiede zwischen DFS und BFS, bin aber interessiert zu wissen, wann es praktischer ist, eine über der anderen zu verwenden.

Könnte jemand Beispiele geben, wie DFS BFS übertrumpfen würde und umgekehrt?


4
Vielleicht könnten Sie die vollständigen Begriffe für DFS und BFS in der Frage erwähnen - die Leute kennen diese Abkürzungen möglicherweise nicht.
Hans-Peter Störr




Anmerkung erwähnt einige Anwendungsszenarien von BFS und DFS
Yossarian42

Antworten:


353

Dies hängt stark von der Struktur des Suchbaums und der Anzahl und Position der Lösungen (auch als gesuchte Elemente bezeichnet) ab.

  • Wenn Sie wissen, dass eine Lösung nicht weit von der Wurzel des Baums entfernt ist, ist eine Breitensuche (BFS) möglicherweise besser.
  • Wenn der Baum sehr tief ist und Lösungen selten sind, kann die Tiefensuche (DFS) extrem lange dauern, BFS kann jedoch schneller sein.

  • Wenn der Baum sehr breit ist, benötigt ein BFS möglicherweise zu viel Speicher, sodass dies möglicherweise völlig unpraktisch ist.

  • Wenn Lösungen häufig sind, sich aber tief im Baum befinden, kann BFS unpraktisch sein.

  • Wenn der Suchbaum sehr tief ist, müssen Sie die Suchtiefe für die Tiefensuche (DFS) ohnehin einschränken (z. B. bei iterativer Vertiefung).

Dies sind jedoch nur Faustregeln. Sie müssen wahrscheinlich experimentieren.


4
Die AFAIK-Rekursion benötigt im Allgemeinen mehr Speicher als die Iteration.
Marek Marczak

3
@MarekMarczak Ich verstehe nicht ganz, was du sagen willst. Wenn Sie BFS als Iteration verwenden - wenn der Lösungsraum nicht leicht aufzählbar ist, müssen Sie möglicherweise die gesamte n-te Ebene des Suchbaums im Speicher speichern, um die n + 1-te Ebene aufzulisten.
Hans-Peter Störr

11
@MarekMarczak Die iterative Version von DFS verwendet einen Stapel. Rekursion vs. Iteration ist ein ganz anderes Thema.
Clint Deygoo

Nur ein weiterer Fall, der mir in den Sinn kam: BFS ist nützlich (notwendig) in einem Fall, in dem ein Graph "unendlich" ist. Wie gesagt, ein Schachbrett, das sich in alle Richtungen bis ins Unendliche erstreckt. DFS wird niemals zurückkehren. BFS findet garantiert, wonach es sucht, wenn die Bedingung erfüllt ist.
ThePartyTurtle

157

Tiefensuche

Tiefensuchen werden häufig in Simulationen von Spielen (und spielähnlichen Situationen in der realen Welt) verwendet. In einem typischen Spiel können Sie eine von mehreren möglichen Aktionen auswählen. Jede Auswahl führt zu weiteren Auswahlmöglichkeiten, von denen jede zu weiteren Auswahlmöglichkeiten führt, und so weiter zu einem ständig wachsenden baumförmigen Diagramm von Möglichkeiten.

Geben Sie hier die Bildbeschreibung ein

Zum Beispiel können Sie sich in Spielen wie Schach, Tic-Tac-Toe, wenn Sie sich für einen Zug entscheiden, mental einen Zug vorstellen, dann die möglichen Antworten Ihres Gegners, dann Ihre Antworten und so weiter. Sie können entscheiden, was zu tun ist, indem Sie sehen, welcher Schritt zum besten Ergebnis führt.

Nur einige Pfade in einem Spielbaum führen zu Ihrem Gewinn. Einige führen zu einem Sieg Ihres Gegners. Wenn Sie ein solches Ende erreichen, müssen Sie einen vorherigen Knoten sichern oder zurückverfolgen und einen anderen Pfad ausprobieren. Auf diese Weise erkunden Sie den Baum, bis Sie einen Pfad mit einem erfolgreichen Abschluss finden. Dann machen Sie den ersten Schritt auf diesem Weg.


Breitensuche

Die Breitensuche hat eine interessante Eigenschaft: Sie findet zuerst alle Scheitelpunkte, die eine Kante vom Startpunkt entfernt sind, dann alle Scheitelpunkte, die zwei Kanten entfernt sind, und so weiter. Dies ist nützlich, wenn Sie versuchen, den kürzesten Pfad vom Startscheitelpunkt zu einem bestimmten Scheitelpunkt zu finden. Sie starten ein BFS und wenn Sie den angegebenen Scheitelpunkt finden, wissen Sie, dass der Pfad, den Sie bisher verfolgt haben, der kürzeste Pfad zum Knoten ist. Wenn es einen kürzeren Weg gäbe, hätte das BFS ihn bereits gefunden.

Die Breitensuche kann verwendet werden, um die Nachbarknoten in Peer-to-Peer-Netzwerken wie BitTorrent, GPS-Systemen zum Auffinden von Standorten in der Nähe, Websites sozialer Netzwerke zum Auffinden von Personen in der angegebenen Entfernung und dergleichen zu finden.


113

Schöne Erklärung von http://www.programmerinterview.com/index.php/data-structures/dfs-vs-bfs/

Ein Beispiel für BFS

Hier ist ein Beispiel dafür, wie ein BFS aussehen würde. Dies ist so etwas wie ein Level Order Tree Traversal, bei dem wir QUEUE mit ITERATIVEM Ansatz verwenden (meistens wird RECURSION mit DFS enden). Die Zahlen geben die Reihenfolge an, in der auf die Knoten in einem BFS zugegriffen wird:

Geben Sie hier die Bildbeschreibung ein

Bei einer ersten Tiefensuche beginnen Sie an der Wurzel und folgen einem der Zweige des Baums so weit wie möglich, bis entweder der gesuchte Knoten gefunden wird oder Sie einen Blattknoten (einen Knoten ohne Kinder) treffen. Wenn Sie einen Blattknoten treffen, setzen Sie die Suche beim nächsten Vorfahren mit unerforschten Kindern fort.

Ein Beispiel für DFS

Hier ist ein Beispiel dafür, wie eine DFS aussehen würde. Ich denke, dass das Durchlaufen der Nachbestellung im Binärbaum zuerst ab der Blattebene beginnt. Die Zahlen geben die Reihenfolge an, in der auf die Knoten in einer DFS zugegriffen wird:

Geben Sie hier die Bildbeschreibung ein

Unterschiede zwischen DFS und BFS

Im Vergleich zu BFS und DFS besteht der große Vorteil von DFS darin, dass der Speicherbedarf wesentlich geringer ist als bei BFS, da nicht alle untergeordneten Zeiger auf jeder Ebene gespeichert werden müssen. Abhängig von den Daten und dem, wonach Sie suchen, kann entweder DFS oder BFS von Vorteil sein.

Wenn man beispielsweise bei einem Stammbaum jemanden auf dem Baum sucht, der noch lebt, kann man davon ausgehen, dass sich diese Person am unteren Rand des Baums befindet. Dies bedeutet, dass ein BFS sehr lange brauchen würde, um dieses letzte Level zu erreichen. Eine DFS würde das Ziel jedoch schneller finden. Aber wenn man nach einem Familienmitglied suchen würde, das vor sehr langer Zeit gestorben ist, dann wäre diese Person näher an der Spitze des Baumes. Dann wäre ein BFS normalerweise schneller als ein DFS. Die Vorteile von beiden variieren also je nach den Daten und dem, wonach Sie suchen.

Ein weiteres Beispiel ist Facebook; Vorschlag für Freunde von Freunden. Wir brauchen sofortige Freunde für Vorschläge, wo wir BFS verwenden können. Möglicherweise finden Sie den kürzesten Weg oder erkennen den Zyklus (mithilfe der Rekursion). Wir können DFS verwenden.


Was ist "Nachbestellungsdurchlauf im Binärbaum"?
Kyle Delaney

8
Findet DFS den kürzesten Weg besser als BFS? Ich denke, es ist umgekehrt. Ich denke, BFS findet den kürzesten Weg. Ist es nicht?
Naveen Gabriel

1
Hätte mehr geschätzt, wenn Sie die Credits an die Quelle gegeben hätten. Der Vergleichsteil wurde von Narasimha Karumanchi aus "Data Structures made in Java" übernommen.
Learntogrow-Growtolearn

Sicher kann ich das aktualisieren, aber ich erwarte hier keine Wertschätzung. Ich möchte nur armen Technikfreaks wie mir helfen.
Kanagavelu Sugumar

1
@KyleDelaney Es gibt drei Ordnungen, in denen Sie einen Baum durchlaufen können - Vorbestellung, Inorder und Postorder. Sie entsprechen der Präfix-Infix- bzw. Postfix-Notation. Wenn Sie den Baum nach unten und dann nach oben durchlaufen, wenn Sie beim ersten Besuch einen Knoten auswählen, der vorbestellt ist, wenn er das zweite Mal in Ordnung ist, wenn er das letzte Mal nachbestellt ist. Sie können den Baum tatsächlich auf diese Weise serialisieren. Solange Sie sich an die von Ihnen verwendete Reihenfolge erinnern, können Sie den Baum aus der Serialisierung neu erstellen.
Dave

43

Die Breitensuche ist im Allgemeinen der beste Ansatz, wenn die Tiefe des Baums variieren kann und Sie nur einen Teil des Baums nach einer Lösung durchsuchen müssen. Das Finden des kürzesten Wegs von einem Startwert zu einem Endwert ist beispielsweise ein guter Ort, um BFS zu verwenden.

Die Tiefensuche wird häufig verwendet, wenn Sie den gesamten Baum durchsuchen müssen. Es ist einfacher zu implementieren (mithilfe von Rekursion) als BFS und erfordert weniger Status: Während BFS erfordert, dass Sie die gesamte 'Grenze' speichern, erfordert DFS nur, dass Sie die Liste der übergeordneten Knoten des aktuellen Elements speichern.


26

DFS ist platzsparender als BFS, kann jedoch in unnötige Tiefen gehen.

Ihre Namen sind aufschlussreich: Wenn es eine große Breite (dh einen großen Verzweigungsfaktor), aber eine sehr begrenzte Tiefe gibt (z. B. eine begrenzte Anzahl von "Zügen"), kann DFS BFS vorzuziehen sein.


Auf IDDFS

Es sollte erwähnt werden, dass es eine weniger bekannte Variante gibt, die die Raumeffizienz von DFS kombiniert, aber (kumulativ) der Besuch von BFS in der Reihenfolge der Ebenen ist die iterative Vertiefung der Tiefensuche . Dieser Algorithmus überprüft einige Knoten erneut, trägt jedoch nur einen konstanten Faktor der asymptotischen Differenz bei.


1
Es ist nicht unbedingt platzsparender. Betrachten Sie beispielsweise ein Pfaddiagramm.
RB

16

Wenn Sie sich dieser Frage als Programmierer nähern, fällt ein Faktor auf: Wenn Sie die Rekursion verwenden, ist die Tiefensuche einfacher zu implementieren, da Sie keine zusätzliche Datenstruktur verwalten müssen, die die noch zu untersuchenden Knoten enthält.

Hier ist die Tiefensuche nach einem nicht orientierten Diagramm, wenn Sie "bereits besuchte" Informationen in den Knoten speichern:

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

Wenn Sie bereits besuchte Informationen in einer separaten Datenstruktur speichern:

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())

Vergleichen Sie dies mit der Breitensuche, bei der Sie eine separate Datenstruktur für die Liste der noch zu besuchenden Knoten verwalten müssen, egal was passiert.



5

Für BFS können wir ein Facebook-Beispiel betrachten. Wir erhalten den Vorschlag, Freunde aus dem FB-Profil aus dem Profil anderer Freunde hinzuzufügen. Angenommen, A-> B, während B-> E und B-> F, so erhält A einen Vorschlag für E und F. Sie müssen BFS verwenden, um bis zur zweiten Ebene zu lesen. DFS basiert eher auf Szenarien, in denen wir etwas basierend auf Daten vorhersagen möchten, die wir von der Quelle bis zum Ziel haben. Wie schon über Schach oder Sudoku erwähnt. Wenn ich hier etwas anderes habe, glaube ich, dass DFS für den kürzesten Weg verwendet werden sollte, da DFS zuerst den gesamten Weg abdeckt, dann können wir den besten entscheiden. Da BFS jedoch den Ansatz von gierig verwendet, sieht es möglicherweise so aus, als wäre es der kürzeste Weg, aber das Endergebnis kann abweichen. Lassen Sie mich wissen, ob mein Verständnis falsch ist.


Jetzt ist mein Kommentar etwas spät. Um den kürzesten Weg zu finden, sollte BFS verwendet werden. "DFS basiert jedoch mehr auf Szenarien, in denen wir etwas basierend auf Daten vorhersagen möchten, die wir von der Quelle bis zum Ziel haben", ist eine brillante Sache, die Sie gesagt haben! Ein dickes Lob !!
Oskarzito

4

Einige Algorithmen hängen von bestimmten Eigenschaften von DFS (oder BFS) ab. Beispielsweise nutzt der Hopcroft- und Tarjan-Algorithmus zum Auffinden von 2 verbundenen Komponenten die Tatsache, dass sich jeder bereits besuchte Knoten, auf den DFS stößt, auf dem Pfad vom Stamm zum aktuell untersuchten Knoten befindet.


4

Das Folgende ist eine umfassende Antwort auf Ihre Fragen.

In einfachen Worten:

Der BFS-Algorithmus (Breadth First Search) erkennt anhand seines Namens "Breadth" alle Nachbarn eines Knotens durch die Außenkanten des Knotens und dann die nicht besuchten Nachbarn der zuvor genannten Nachbarn durch ihre Außenkanten usw. bis auf alle Die von der ursprünglichen Quelle aus erreichbaren Knoten werden besucht (wir können fortfahren und eine andere ursprüngliche Quelle verwenden, wenn noch nicht besuchte Knoten usw. vorhanden sind). Aus diesem Grund kann es verwendet werden, um den kürzesten Weg (falls vorhanden) von einem Knoten (ursprüngliche Quelle) zu einem anderen Knoten zu finden, wenn die Gewichte der Kanten gleichmäßig sind.

Der DFS-Algorithmus (Depth First Search) erkennt anhand seines Namens "Depth" die nicht besuchten Nachbarn des zuletzt entdeckten Knotens x an seinen Außenkanten. Wenn es keinen nicht besuchten Nachbarn vom Knoten x gibt, geht der Algorithmus zurück, um die nicht besuchten Nachbarn des Knotens (durch seine Außenkanten) zu ermitteln, von denen aus der Knoten x entdeckt wurde, usw., bis alle von der ursprünglichen Quelle erreichbaren Knoten besucht sind (Wir können fortfahren und eine andere ursprüngliche Quelle verwenden, wenn noch nicht besuchte Knoten usw. vorhanden sind.)

Sowohl BFS als auch DFS können unvollständig sein. Wenn beispielsweise der Verzweigungsfaktor eines Knotens unendlich ist oder für die zu unterstützenden Ressourcen (Speicher) sehr groß ist (z. B. beim Speichern der als nächstes zu erkennenden Knoten), ist BFS nicht vollständig, obwohl sich der gesuchte Schlüssel in einiger Entfernung befinden kann von wenigen Kanten aus der ursprünglichen Quelle. Dieser unendliche Verzweigungsfaktor kann auf unendliche Auswahlmöglichkeiten (benachbarte Knoten) eines bestimmten Knotens zurückzuführen sein. Wenn die Tiefe unendlich ist oder für die zu unterstützenden Ressourcen (Speicher) sehr groß ist (z. B. beim Speichern der Knoten, die als nächstes entdeckt werden sollen), ist DFS nicht vollständig, obwohl der gesuchte Schlüssel der dritte Nachbar der ursprünglichen Quelle sein kann. Diese unendliche Tiefe kann auf eine Situation zurückzuführen sein, in der für jeden Knoten, den der Algorithmus entdeckt, mindestens eine neue Auswahl (benachbarter Knoten) vorhanden ist, die zuvor nicht besucht wurde.

Daher können wir schließen, wann BFS und DFS verwendet werden sollen. Angenommen, es handelt sich um einen überschaubaren begrenzten Verzweigungsfaktor und eine überschaubare begrenzte Tiefe. Wenn der gesuchte Knoten flach ist, dh nach einigen Kanten von der ursprünglichen Quelle erreichbar ist, ist es besser, BFS zu verwenden. Wenn der gesuchte Knoten jedoch tief ist, dh nach vielen Kanten von der ursprünglichen Quelle aus erreichbar ist, ist es besser, DFS zu verwenden.

Wenn wir beispielsweise in einem sozialen Netzwerk nach Personen suchen möchten, die ähnliche Interessen einer bestimmten Person haben, können wir BFS von dieser Person als ursprüngliche Quelle anwenden, da diese Personen meistens seine direkten Freunde oder Freunde von Freunden sind, dh eine oder zwei Kanten weit. Wenn wir andererseits nach Personen suchen möchten, die völlig unterschiedliche Interessen einer bestimmten Person haben, können wir die DFS dieser Person als ursprüngliche Quelle verwenden, da diese Personen meistens sehr weit von ihm entfernt sind, dh Freund eines Freundes eines Freundes .... dh zu viele Kanten weit.

Die Anwendungen von BFS und DFS können auch aufgrund des Suchmechanismus in den einzelnen Anwendungen variieren. Zum Beispiel können wir entweder BFS (vorausgesetzt, der Verzweigungsfaktor ist verwaltbar) oder DFS (vorausgesetzt, die Tiefe ist verwaltbar) verwenden, wenn wir nur die Erreichbarkeit von einem Knoten zum anderen überprüfen möchten, ohne Informationen darüber zu haben, wo sich dieser Knoten befinden kann. Außerdem können beide die gleichen Aufgaben wie die topologische Sortierung eines Graphen lösen (falls vorhanden). BFS kann verwendet werden, um den kürzesten Weg mit Kanten mit Einheitsgewicht von einem Knoten (ursprüngliche Quelle) zu einem anderen zu finden. Während DFS verwendet werden kann, um alle Auswahlmöglichkeiten zu erschöpfen, da es sich um eine Tiefenwirkung handelt, z. B. um den längsten Pfad zwischen zwei Knoten in einem azyklischen Diagramm zu ermitteln. Auch DFS kann zur Zykluserkennung in einem Diagramm verwendet werden.

Wenn wir eine unendliche Tiefe und einen unendlichen Verzweigungsfaktor haben, können wir letztendlich die Iterative Deepening Search (IDS) verwenden.


2

Entsprechend den Eigenschaften von DFS und BFS. Zum Beispiel, wenn wir den kürzesten Weg finden wollen. wir verwenden normalerweise bfs, es kann das 'kürzeste' garantieren. Aber dfs kann nur garantieren, dass wir von diesem Punkt kommen können, kann diesen Punkt erreichen, kann nicht den "kürzesten" garantieren.


2

Ich denke, es hängt davon ab, mit welchen Problemen Sie konfrontiert sind.

  1. kürzester Weg auf einfachem Graphen -> bfs
  2. alle möglichen ergebnisse -> dfs
  3. Suche im Diagramm (behandle Baum, Martix auch als Diagramm) -> dfs ....

Wenn Sie vor der Liste eine leere Zeile einfügen, sieht die Antwort viel besser aus.
Montonero

1

Da bei der Tiefensuche bei der Verarbeitung der Knoten ein Stapel verwendet wird, wird das Backtracking mit DFS bereitgestellt. Da die Breitensuche eine Warteschlange und keinen Stapel verwendet, um zu verfolgen, welche Knoten verarbeitet werden, wird mit BFS kein Backtracking bereitgestellt.


1

Wenn die Baumbreite sehr groß und die Tiefe gering ist, verwenden Sie DFS, da der Rekursionsstapel nicht überläuft. Verwenden Sie BFS, wenn die Breite gering und die Tiefe sehr groß ist, um den Baum zu durchlaufen.


0

Dies ist ein gutes Beispiel, um zu demonstrieren, dass BFS in bestimmten Fällen besser als DFS ist. https://leetcode.com/problems/01-matrix/

Bei korrekter Implementierung sollten beide Lösungen Zellen besuchen, die weiter entfernt sind als die aktuelle Zelle +1. DFS ist jedoch ineffizient und hat wiederholt dieselbe Zelle besucht, was zu einer O (n * n) -Komplexität führt.

Zum Beispiel,

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

Dies hängt von der Situation ab, in der es verwendet wird. Wenn wir Probleme beim Durchlaufen eines Diagramms haben, tun wir dies aus irgendeinem Grund. Wenn es ein Problem gibt, den kürzesten Pfad in einem ungewichteten Diagramm zu finden oder festzustellen, ob ein Diagramm zweiteilig ist, können wir BFS verwenden. Bei Problemen mit der Zykluserkennung oder bei Logik, die ein Backtracking erfordert, können wir DFS verwenden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.