Quando è pratico utilizzare Depth-First Search (DFS) vs Breadth-First Search (BFS)? [chiuso]


345

Comprendo le differenze tra DFS e BFS, ma sono interessato a sapere quando è più pratico utilizzarne uno rispetto all'altro?

Qualcuno potrebbe fare qualche esempio di come DFS avrebbe battuto BFS e viceversa?


4
Forse potresti menzionare i termini completi di DFS e BFS alla domanda: le persone potrebbero non conoscere queste abbreviazioni.
Hans-Peter Störr,




note menziona alcuni scenari applicativi di BFS e DFS
Yossarian42

Risposte:


353

Ciò dipende in larga misura dalla struttura dell'albero di ricerca e dal numero e dalla posizione delle soluzioni (ovvero elementi ricercati).

  • Se sai che una soluzione non è lontana dalla radice dell'albero, una prima ricerca di ampiezza (BFS) potrebbe essere migliore.
  • Se l'albero è molto profondo e le soluzioni sono rare, la prima ricerca di profondità (DFS) potrebbe richiedere molto tempo, ma BFS potrebbe essere più veloce.

  • Se l'albero è molto largo, un BFS potrebbe aver bisogno di troppa memoria, quindi potrebbe essere completamente poco pratico.

  • Se le soluzioni sono frequenti ma situate in profondità nella struttura ad albero, la BFS potrebbe non essere pratica.

  • Se l'albero di ricerca è molto profondo, sarà comunque necessario limitare la profondità di ricerca per la prima ricerca di profondità (DFS), ad esempio (ad esempio con approfondimento iterativo).

Ma queste sono solo regole empiriche; probabilmente dovrai sperimentare.


4
La ricorsione di AFAIK richiede generalmente più memoria che iterazione.
Marek Marczak,

3
@MarekMarczak Non capisco bene cosa vuoi dire. Se si considera BFS come iterazione, se lo spazio della soluzione non è facilmente enumerabile, potrebbe essere necessario memorizzare l'intero n-esimo livello dell'albero di ricerca in memoria per enumerare il n + 1 ° livello.
Hans-Peter Störr,

11
@MarekMarczak La versione iterativa di DFS utilizza uno stack. Ricorsione vs Iterazione è un argomento completamente separato.
Clint Deygoo,

Solo un altro caso che mi è venuto in mente: BFS è utile (necessario) in un caso in cui un grafico è "infinito". Come dire, una scacchiera che si estende all'infinito in tutte le direzioni. DFS non tornerà mai più. BFS è garantito per trovare ciò che sta cercando SE la condizione è soddisfacente.
ThePartyTurtle

157

Ricerca approfondita

Le ricerche in base alla profondità vengono spesso utilizzate nelle simulazioni di giochi (e situazioni di gioco simili nel mondo reale). In un gioco tipico puoi scegliere una delle diverse azioni possibili. Ogni scelta porta a ulteriori scelte, ognuna delle quali porta a ulteriori scelte, e così via in un grafico delle possibilità a forma di albero in continua espansione.

inserisci qui la descrizione dell'immagine

Ad esempio in giochi come gli scacchi, tic-tac-toe quando decidi quale mossa fare, puoi immaginare mentalmente una mossa, quindi le possibili risposte del tuo avversario, quindi le tue risposte e così via. Puoi decidere cosa fare vedendo quale mossa porta al miglior risultato.

Solo alcuni percorsi in un albero di gioco portano alla tua vittoria. Alcuni portano a una vittoria da parte del tuo avversario, quando raggiungi un tale finale, devi tornare indietro o tornare indietro su un nodo precedente e provare un percorso diverso. In questo modo esplori l'albero fino a trovare un percorso con una conclusione positiva. Quindi fai la prima mossa lungo questo percorso.


Ampia ricerca

La ricerca in ampiezza ha una proprietà interessante: trova prima tutti i vertici che si trovano a un bordo dal punto iniziale, quindi tutti i vertici che sono a due bordi e così via. Questo è utile se stai cercando di trovare il percorso più breve dal vertice iniziale a un dato vertice. Inizi un BFS e quando trovi il vertice specificato, sai che il percorso che hai tracciato finora è il percorso più breve al nodo. Se ci fosse un percorso più breve, il BFS lo avrebbe già trovato.

L'ampia ricerca può essere utilizzata per trovare i nodi vicini in reti peer to peer come BitTorrent, sistemi GPS per trovare posizioni vicine, siti di social network per trovare persone alla distanza specificata e cose del genere.


113

Bella spiegazione da http://www.programmerinterview.com/index.php/data-structures/dfs-vs-bfs/

Un esempio di BFS

Ecco un esempio di come sarebbe un BFS. Questo è qualcosa come Level Order Tree Traversal in cui utilizzeremo la QUEUE con approccio ITERATIVO (Principalmente RECURSION finirà con DFS). I numeri rappresentano l'ordine in cui si accede ai nodi in un BFS:

inserisci qui la descrizione dell'immagine

In una prima ricerca approfondita, inizi dalla radice e segui uno dei rami dell'albero il più possibile finché non viene trovato il nodo che stai cercando o colpisci un nodo foglia (un nodo senza figli). Se colpisci un nodo foglia, continua la ricerca all'antenato più vicino con figli inesplorati.

Un esempio di DFS

Ecco un esempio di come sarebbe un DFS. Penso che l'attraversamento dell'ordine postale nell'albero binario inizierà prima a lavorare dal livello Foglia. I numeri rappresentano l'ordine in cui si accede ai nodi in un DFS:

inserisci qui la descrizione dell'immagine

Differenze tra DFS e BFS

Confrontando BFS e DFS, il grande vantaggio di DFS è che ha requisiti di memoria molto più bassi rispetto a BFS, perché non è necessario archiviare tutti i puntatori figlio ad ogni livello. A seconda dei dati e di ciò che stai cercando, DFS o BFS potrebbero essere vantaggiosi.

Ad esempio, dato un albero genealogico se uno cercasse qualcuno sull'albero che è ancora vivo, allora sarebbe lecito ritenere che quella persona si troverebbe sul fondo dell'albero. Ciò significa che un BFS richiederebbe molto tempo per raggiungere l'ultimo livello. Un DFS, tuttavia, troverebbe l'obiettivo più velocemente. Ma se si cercasse un membro della famiglia morto molto tempo fa, quella persona sarebbe più vicina alla cima dell'albero. Quindi, un BFS sarebbe di solito più veloce di un DFS. Quindi, i vantaggi di entrambi variano a seconda dei dati e di ciò che stai cercando.

Un altro esempio è Facebook; Suggerimento su Friends of Friends. Abbiamo bisogno di amici immediati per suggerimenti su dove possiamo usare BFS. Può essere trovare il percorso più breve o rilevare il ciclo (usando la ricorsione) che possiamo usare DFS.


Che cos'è "attraversamento ordine postale nell'albero binario"?
Kyle Delaney,

8
DFS trova il percorso più breve migliore di BFS? Penso che sia il contrario. Penso che BFS trovi il percorso più breve. No?
Naveen Gabriel,

1
Avresti apprezzato di più se avessi dato i crediti alla fonte. La parte di confronto è tratta da "Strutture dati rese facili in Java" da Narasimha Karumanchi.
learntogrow-growtolearn

Sicuramente posso aggiornarlo, ma non mi aspetto che qualcuno apprezzi qui. Voglio solo aiutare il povero tecnico come me.
Kanagavelu Sugumar,

1
@KyleDelaney ci sono tre ordini in cui puoi attraversare un albero: preordine, inorder e postorder. Corrispondono rispettivamente alla notazione di prefisso e postfisso. Quando attraversi l'albero verso il basso e poi esegui il backup, se scegli un nodo la prima volta che lo visiti, è in preordine, se la seconda volta è in ordine, se l'ultima è in postordine. Puoi effettivamente serializzare l'albero in questo modo e finché ti ricordi l'ordine che hai usato puoi ricostruire l'albero dalla serializzazione.
Dave,

43

Breadth First Search è generalmente l'approccio migliore quando la profondità dell'albero può variare e devi solo cercare una parte dell'albero per trovare una soluzione. Ad esempio, trovare il percorso più breve da un valore iniziale a un valore finale è un buon posto per usare BFS.

Profondità Prima ricerca viene comunemente utilizzata quando è necessario cercare l'intero albero. È più facile da implementare (usando la ricorsione) rispetto a BFS e richiede meno stato: mentre BFS richiede di memorizzare l'intera "frontiera", DFS richiede solo di memorizzare l'elenco dei nodi padre dell'elemento corrente.


26

DFS è più efficiente in termini di spazio di BFS, ma potrebbe andare a profondità non necessarie.

I loro nomi sono rivelatori: se c'è una grande ampiezza (cioè un grande fattore di ramificazione), ma una profondità molto limitata (ad esempio un numero limitato di "mosse"), allora DFS può essere più preferibile a BFS.


Su IDDFS

Va detto che esiste una variante meno conosciuta che combina l'efficienza spaziale di DFS, ma (cumulativamente) la visita di ordine di livello di BFS, è la ricerca iterativa di approfondimento approfondita prima . Questo algoritmo rivisita alcuni nodi, ma contribuisce solo a un fattore costante di differenza asintotica.


1
Non è necessariamente più efficiente in termini di spazio. Ad esempio, considera un grafico del percorso.
RB

16

Quando si affronta questa domanda come programmatore, emerge un fattore: se si utilizza la ricorsione, la ricerca approfondita è più semplice da implementare, poiché non è necessario mantenere una struttura di dati aggiuntiva contenente i nodi ancora da esplorare.

Ecco la ricerca approfondita di un grafico non orientato se si memorizzano informazioni "già visitate" nei nodi:

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

Se si memorizzano informazioni "già visitate" in una struttura di dati separata:

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

Contrastalo con la ricerca per la prima volta in cui è necessario mantenere una struttura di dati separata per l'elenco dei nodi che devono ancora visitare, qualunque cosa accada.



5

Per BFS, possiamo considerare l'esempio di Facebook. Riceviamo suggerimenti per aggiungere amici dal profilo FB da altri profili amici. Supponiamo che A-> B, mentre B-> E e B-> F, quindi A otterrà un suggerimento per E e F. Devono usare BFS per leggere fino al secondo livello. DFS è più basato su scenari in cui vogliamo prevedere qualcosa in base ai dati che abbiamo dall'origine alla destinazione. Come già accennato su scacchi o sudoku. Una volta che la cosa che ho di diverso qui è, credo che DFS dovrebbe essere usato per il percorso più breve perché DFS coprirà prima l'intero percorso, quindi possiamo decidere il meglio. Ma poiché BFS utilizzerà l'approccio avido, potrebbe darsi che sia il percorso più breve, ma il risultato finale potrebbe essere diverso. Fammi sapere se la mia comprensione è sbagliata.


Ora il mio commento è un po 'in ritardo. Ma per trovare il percorso più breve, è necessario utilizzare BFS. Tuttavia, "DFS è più basato su scenari in cui vogliamo prevedere qualcosa in base ai dati che abbiamo dall'origine alla destinazione" è una cosa brillante che hai detto! Complimenti !!
Oskarzito,

4

Alcuni algoritmi dipendono da particolari proprietà di DFS (o BFS) per funzionare. Ad esempio, l'algoritmo Hopcroft e Tarjan per la ricerca di componenti a 2 connessioni sfrutta il fatto che ogni nodo già visitato incontrato da DFS si trova sul percorso dalla radice al nodo attualmente esplorato.


4

Di seguito è una risposta completa a ciò che stai chiedendo.

In parole povere:

L'algoritmo Breadth First Search (BFS), dal suo nome "Breadth", scopre tutti i vicini di un nodo attraverso i bordi esterni del nodo, quindi scopre i vicini non visitati dei vicini precedentemente menzionati attraverso i loro bordi esterni e così via, fino a quando tutto vengono visitati i nodi raggiungibili dalla fonte originale (possiamo continuare e prendere un'altra fonte originale se rimangono nodi non visitati e così via). Ecco perché può essere utilizzato per trovare il percorso più breve (se presente) da un nodo (sorgente originale) a un altro nodo se i pesi dei bordi sono uniformi.

L'algoritmo Depth First Search (DFS), dal suo nome "Depth", scopre i vicini non visitati del nodo x scoperto più di recente attraverso i suoi bordi esterni. Se non esiste un vicino non visitato dal nodo x, l'algoritmo fa un passo indietro per scoprire i vicini non visitati del nodo (attraverso i suoi bordi esterni) da cui il nodo x è stato scoperto, e così via, fino a quando tutti i nodi raggiungibili dalla fonte originale sono visitati (possiamo continuare e prendere un'altra fonte originale se rimangono nodi non visitati e così via).

Sia BFS che DFS possono essere incompleti. Ad esempio se il fattore di diramazione di un nodo è infinito o molto grande per le risorse (memoria) da supportare (ad es. Quando si memorizzano i nodi da scoprire successivamente), allora BFS non è completo anche se la chiave cercata può essere a distanza di pochi spigoli dalla fonte originale. Questo fattore di ramificazione infinita può essere dovuto a infinite scelte (nodi vicini) da un determinato nodo da scoprire. Se la profondità è infinita o molto grande per le risorse (memoria) da supportare (ad es. Quando si memorizzano i nodi da scoprire in seguito), DFS non è completo anche se la chiave cercata può essere il terzo vicino dell'origine originale. Questa profondità infinita può essere dovuta a una situazione in cui esiste, per ogni nodo che scopre l'algoritmo, almeno una nuova scelta (nodo adiacente) che non è mai stata vista prima.

Pertanto, possiamo concludere quando utilizzare BFS e DFS. Supponiamo di avere a che fare con un fattore di ramificazione limitato gestibile e una profondità limitata gestibile. Se il nodo cercato è superficiale, cioè raggiungibile dopo alcuni bordi dalla sorgente originale, è meglio usare BFS. D'altra parte, se il nodo cercato è profondo, cioè raggiungibile dopo molti spigoli dalla sorgente originale, allora è meglio usare DFS.

Ad esempio, in un social network se vogliamo cercare persone che hanno interessi simili a una persona specifica, possiamo applicare BFS da questa persona come fonte originale, perché principalmente queste persone saranno i suoi amici diretti o amici di amici, cioè uno o due bordi lontani. D'altra parte, se vogliamo cercare persone che hanno interessi completamente diversi di una persona specifica, possiamo applicare DFS da questa persona come fonte originale, perché per lo più queste persone saranno molto lontane da lui, ovvero amico di un amico di un amico .... cioè troppi bordi lontani.

Le applicazioni di BFS e DFS possono variare anche a causa del meccanismo di ricerca in ciascuna di esse. Ad esempio, possiamo usare BFS (supponendo che il fattore di diramazione sia gestibile) o DFS (supponendo che la profondità sia gestibile) quando vogliamo solo verificare la raggiungibilità da un nodo all'altro senza avere informazioni su dove possa trovarsi quel nodo. Inoltre, entrambi possono risolvere gli stessi compiti dell'ordinamento topologico di un grafico (se presente). BFS può essere utilizzato per trovare il percorso più breve, con bordi di peso unitario, da un nodo (sorgente originale) a un altro. Considerando che DFS può essere utilizzato per esaurire tutte le scelte a causa della sua natura di andare in profondità, come scoprire il percorso più lungo tra due nodi in un grafico aciclico. Anche DFS, può essere utilizzato per il rilevamento di cicli in un grafico.

Alla fine, se abbiamo una profondità infinita e un fattore di ramificazione infinito, possiamo usare Iterative Deepening Search (IDS).


2

Secondo le proprietà di DFS e BFS. Ad esempio, quando vogliamo trovare il percorso più breve. usiamo solitamente bfs, può garantire il 'più breve'. ma solo dfs può garantire che possiamo venire da questo punto in grado di raggiungere quel punto, non può garantire il 'più breve'.


2

Penso che dipenda da quali problemi stai affrontando.

  1. percorso più breve sul grafico semplice -> bfs
  2. tutti i possibili risultati -> dfs
  3. cerca nel grafico (tratta anche l'albero, martix come un grafico) -> dfs ....

Se aggiungi una riga vuota prima dell'elenco, la risposta avrà un aspetto molto migliore.
Montonero,

1

Poiché le ricerche Depth-First utilizzano uno stack mentre i nodi vengono elaborati, il backtracking viene fornito con DFS. Poiché le Ricerche Breadth-First utilizzano una coda, non uno stack, per tenere traccia dei nodi elaborati, il backtracking non viene fornito con BFS.


1

Quando la larghezza dell'albero è molto grande e la profondità è bassa, utilizzare DFS poiché lo stack di ricorsione non trabocca. Utilizzare BFS quando la larghezza è bassa e la profondità è molto grande per attraversare l'albero.


0

Questo è un buon esempio per dimostrare che BFS è migliore di DFS in alcuni casi. https://leetcode.com/problems/01-matrix/

Se implementate correttamente, entrambe le soluzioni dovrebbero visitare celle che hanno una distanza maggiore rispetto alla cella corrente +1. Ma DFS è inefficiente e ha ripetutamente visitato la stessa cella risultante complessità O (n * n).

Per esempio,

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

Dipende dalla situazione in cui viene utilizzato. Ogni volta che abbiamo un problema di attraversamento di un grafico, lo facciamo per qualche scopo. Quando c'è un problema nel trovare il percorso più breve in un grafico non ponderato o nel scoprire se un grafico è bipartito, possiamo usare BFS. Per problemi di rilevamento del ciclo o qualsiasi logica che richiede il backtracking, possiamo usare DFS.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.