Je comprends les différences entre DFS et BFS, mais j'aimerais savoir s'il est plus pratique d'utiliser l'un par rapport à l'autre?
Quelqu'un pourrait-il donner des exemples montrant comment DFS l'emporterait sur BFS et inversement?
Cela dépend en grande partie de la structure de l'arbre de recherche et du nombre et de l'emplacement des solutions (ou éléments recherchés).
Si l'arborescence est très profonde et que les solutions sont rares, la recherche d'abord en profondeur (DFS) peut prendre un temps extrêmement long, mais BFS peut être plus rapide.
Si l'arborescence est très large, un système de fichiers BFS peut nécessiter trop de mémoire, de sorte qu'il peut s'avérer totalement impraticable.
Si les solutions sont fréquentes mais situées au plus profond de l’arbre, BFS pourrait ne pas être pratique.
Mais ce ne sont que des règles empiriques; vous aurez probablement besoin d'expérimenter.
Les recherches en profondeur d'abord sont souvent utilisées dans des simulations de jeux (et de situations similaires à des jeux dans le monde réel). Dans un jeu typique, vous pouvez choisir l’une des actions possibles. Chaque choix conduit à des choix ultérieurs, chacun conduisant à des choix ultérieurs, et ainsi de suite, à un graphe de possibilités en expansion constante.
Par exemple, dans des jeux comme les échecs, tic-tac-toe, lorsque vous décidez quel mouvement vous souhaitez faire, vous pouvez imaginer mentalement un mouvement, puis les réponses possibles de votre adversaire, puis vos réponses, etc. Vous pouvez décider quoi faire en voyant quel mouvement conduit au meilleur résultat.
Seuls quelques chemins dans un arbre de jeu mènent à votre victoire. Certains mènent à une victoire de votre adversaire, lorsque vous atteignez une telle fin, vous devez sauvegarder ou revenir en arrière sur un nœud précédent et essayer un autre chemin. De cette façon, vous explorez l’arbre jusqu’à ce que vous trouviez un chemin aboutissant. Ensuite, vous faites le premier pas le long de ce chemin.
La recherche largeur-premier a une propriété intéressante: elle trouve d'abord tous les sommets situés à un bord du point de départ, puis tous les sommets situés à deux bords, etc. Ceci est utile si vous essayez de trouver le chemin le plus court du sommet au point de départ. Vous commencez un BFS et lorsque vous trouvez le sommet spécifié, vous savez que le chemin que vous avez suivi jusqu’à présent est le plus court chemin vers le nœud. S'il y avait un chemin plus court, le BFS l'aurait déjà trouvé.
La recherche en largeur d'abord peut être utilisée pour trouver les nœuds voisins dans les réseaux d'égal à égal comme BitTorrent, les systèmes GPS pour trouver des emplacements à proximité, les sites de réseaux sociaux pour trouver des personnes à une distance spécifiée, etc.
Belle explication de http://www.programmerinterview.com/index.php/data-structures/dfs-vs-bfs/
Un exemple de BFS
Voici un exemple de ce à quoi un BFS ressemblerait. Cela ressemble à quelque chose du type Traversée de l’ordre des commandes où nous utiliserons l’approche QUEUE avec ITERATIVE (principalement RECURSION aboutira à DFS). Les nombres représentent l'ordre dans lequel les nœuds sont accédés dans un BFS:
Dans une première recherche en profondeur, vous commencez à la racine et suivez l’une des branches de l’arbre le plus loin possible jusqu’à trouver le nœud que vous recherchez ou à frapper un nœud feuille (un nœud sans enfants). Si vous atteignez un nœud feuille, vous continuez la recherche chez l'ancêtre le plus proche avec des enfants non explorés.
Un exemple de DFS
Voici un exemple de ce à quoi ressemblerait un DFS. Je pense que la traversée post-commande dans l’arbre binaire commencera par le travail à partir du niveau Feuille. Les nombres représentent l'ordre dans lequel les nœuds sont accédés dans un DFS:
Différences entre DFS et BFS
En comparant BFS et DFS, le gros avantage de DFS est qu’il nécessite beaucoup moins de mémoire que BFS, car il n’est pas nécessaire de stocker tous les pointeurs enfants à chaque niveau. Selon les données et ce que vous recherchez, DFS ou BFS peuvent être avantageux.
Par exemple, si on cherchait un arbre généalogique si on cherchait une personne encore en vie sur l'arbre, il serait prudent de supposer que cette personne se trouverait au bas de l'arbre. Cela signifie qu’un BFS mettrait très longtemps à atteindre ce dernier niveau. Un DFS, cependant, trouverait l’objectif plus rapidement. Mais si on cherchait un membre de la famille décédé il y a très longtemps, cette personne serait alors plus proche du sommet de l'arbre. Ensuite, un système BFS serait généralement plus rapide qu'un système DFS. Ainsi, les avantages de l’un ou de l’autre varient en fonction des données et de ce que vous recherchez.
Un autre exemple est Facebook; Suggestion sur les amis des amis. Nous avons besoin d'amis immédiats pour suggérer où nous pouvons utiliser BFS. Peut-être trouver le chemin le plus court ou détecter le cycle (en utilisant la récursion), nous pouvons utiliser DFS.
L'étendue de la recherche en premier lieu est généralement la meilleure approche lorsque la profondeur de l'arbre peut varier et qu'il suffit de rechercher une partie de l'arbre pour trouver une solution. Par exemple, trouver le chemin le plus court entre une valeur de départ et une valeur finale est un bon endroit pour utiliser BFS.
La recherche en profondeur d'abord est couramment utilisée lorsque vous devez effectuer une recherche dans tout l'arborescence. Il est plus facile à implémenter (à l'aide de la récursivité) que BFS et nécessite moins d'état: alors que BFS nécessite de stocker l'intégralité de la "frontière", DFS requiert uniquement de stocker la liste des nœuds parents de l'élément actuel.
DFS est plus efficace en termes d'espace que BFS, mais peut atteindre des profondeurs inutiles.
Leurs noms sont révélateurs: s’il ya une grande largeur (c’est-à-dire un grand facteur de ramification), mais une profondeur très limitée (par exemple, un nombre limité de "déménagements"), alors DFS peut être plus préférable que BFS.
Il convient de mentionner qu’il existe une variante moins connue combinant l’efficacité d’utilisation de l’espace de DFS, mais (de manière cumulative) la visite par ordre de niveau de BFS, est le recherche itérative d’approfondissement en profondeur . Cet algorithme revisite certains nœuds, mais il ne contribue qu’à un facteur constant de différence asymptotique.
Lorsque vous abordez cette question en tant que programmeur, un facteur ressort: si vous utilisez la récursivité, la recherche en profondeur d'abord est plus simple à implémenter, car vous n'avez pas besoin de conserver une structure de données supplémentaire. contenant les nœuds encore à explorer.
Voici une recherche en profondeur d'un graphe non orienté si vous stockez des informations "déjà visitées" dans les nœuds:
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
Si vous stockez des informations "déjà visitées" dans une structure de données séparée:
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())
Comparez cela à la recherche en largeur d'abord où vous devez conserver une structure de données distincte pour la liste des nœuds à visiter, quoi qu'il en soit.
Un avantage important de BFS serait qu'il peut être utilisé pour trouver le chemin le plus court entre deux nœuds quelconques dans un graphique non pondéré. Considérant que, nous ne pouvons pas utiliser DFS pour le même .
Pour BFS, on peut considérer l'exemple de Facebook. Nous recevons la suggestion d'ajouter des amis à partir du profil FB d'un autre profil d'amis. Supposons que A-> B, tandis que B-> E et B-> F, donc A recevra une suggestion pour E et F. Ils doivent utiliser BFS pour lire jusqu'au deuxième niveau. DFS est davantage basé sur des scénarios dans lesquels nous souhaitons prévoir quelque chose en fonction des données dont nous disposons, de la source à la destination. Comme déjà mentionné sur les échecs ou le sudoku. Une fois que j’ai une différence, c’est que je pense que DFS devrait être utilisé pour le chemin le plus court, car DFS couvrira tout le chemin en premier pour que nous puissions choisir le meilleur. Mais comme BFS utilisera l'approche de greedy, cela peut sembler être le chemin le plus court, mais le résultat final peut différer. Faites-moi savoir si ma compréhension est fausse.
Certains algorithmes dépendent de propriétés particulières de DFS (ou BFS) pour fonctionner. Par exemple, l'algorithme Hopcroft et Tarjan pour rechercher des composants connectés en double tire parti du fait que chaque nœud déjà visité rencontré par DFS se trouve sur le chemin allant de la racine au nœud actuellement exploré.
Selon les propriétés de DFS et BFS. Par exemple, lorsque nous voulons trouver le chemin le plus court. nous utilisons généralement bfs, il peut garantir le "plus court". mais seulement dfs peut garantir que nous pouvons venir de ce point peut atteindre ce point, ne peut pas garantir le "plus court".
Lorsque la largeur de l'arbre est très grande et que la profondeur est faible, utilisez DFS car la pile de récurrence ne débordera pas. Utilisez BFS lorsque la largeur est faible et que la profondeur est très grande pour traverser l'arbre.
Je pense que cela dépend des problèmes auxquels vous êtes confrontés.
Etant donné que les recherches en profondeur d'abord utilisent une pile lors du traitement des nœuds, le retour en arrière est fourni avec DFS. Etant donné que les recherches en largeur d'abord utilisent une file d'attente, et non une pile, pour garder trace des nœuds traités, le retour en arrière n'est pas fourni avec BFS.
En termes simples:
L'algorithme BFS (Breadth First Search), depuis son nom "Largeur", découvre tous les voisins d'un nœud à travers les bords extérieurs du nœud, puis il découvre les voisins non visités des voisins mentionnés précédemment à travers leurs bords extérieurs, etc. les nœuds accessibles depuis la source d'origine sont visités (nous pouvons continuer et prendre une autre source d'origine s'il reste des nœuds non visités, etc.). C'est pourquoi il peut être utilisé pour trouver le chemin le plus court (s'il y en a un) d'un nœud (source d'origine) vers un autre nœud si le poids des bords est uniforme.
L'algorithme Depth First Search (DFS), sous son nom "Depth", découvre les voisins non visités du dernier nœud x découvert par ses bords extérieurs. S'il n'y a pas de voisin non visité du nœud x, l'algorithme effectue un retour arrière pour découvrir les voisins non consultés du nœud (par ses bords extérieurs) à partir desquels le nœud x a été découvert, et ainsi de suite, jusqu'à ce que tous les nœuds accessibles à partir de la source d'origine soient visités. (Nous pouvons continuer et prendre une autre source d'origine s'il reste des nœuds non visités, etc.).
BFS et DFS peuvent être incomplets. Par exemple, si le facteur de branchement d’un nœud est infini ou très important pour les ressources (mémoire) à prendre en charge (par exemple lors du stockage des nœuds à découvrir ultérieurement), BFS n’est pas complet même si la clé recherchée peut être distante. de quelques bords de la source d'origine. Ce facteur de branchement infini peut être dû aux choix infinis (nœuds voisins) à partir d’un nœud donné à découvrir. Si la profondeur est infinie ou très importante pour les ressources (mémoire) à prendre en charge (par exemple, lors du stockage des nœuds à découvrir ensuite), la DFS n'est pas complète, même si la clé recherchée peut être le troisième voisin de la source d'origine. Cette profondeur infinie peut être due à une situation où il y a, pour chaque nœud découvert par l'algorithme, au moins un nouveau choix (nœud voisin) non visité auparavant.
Par conséquent, nous pouvons conclure quand utiliser BFS et DFS. Supposons que nous ayons affaire à un facteur de ramification limité gérable et à une profondeur limitée gérable. Si le noeud recherché est peu profond, c'est-à-dire accessible après quelques contours de la source d'origine, il est préférable d'utiliser BFS. D’autre part, si le noeud recherché est profond, c’est-à-dire accessible après de nombreux contours de la source d'origine, il est préférable d'utiliser DFS.
Par exemple, dans un réseau social, si nous souhaitons rechercher des personnes partageant les mêmes intérêts qu’une personne en particulier, nous pouvons appliquer BFS de cette personne en tant que source originale, car ces personnes seront généralement ses amis directs ou ses amis, ou deux bords loin. D'autre part, si nous souhaitons rechercher des personnes ayant des intérêts complètement différents d'une personne spécifique, nous pouvons appliquer DFS de cette personne en tant que source d'origine, car ces personnes seront généralement très éloignées de lui, c'est-à-dire ami d'un ami d'ami .... c'est-à-dire trop de bords loin.
Les applications de BFS et DFS peuvent également varier en raison du mécanisme de recherche utilisé dans chacune d’elles. Par exemple, nous pouvons utiliser BFS (en supposant que le facteur de branchement est gérable) ou DFS (en supposant que la profondeur soit gérable) lorsque nous voulons simplement vérifier l'accessibilité d'un nœud à un autre sans aucune information là où ce nœud peut être. Les deux peuvent également résoudre les mêmes tâches, telles que le tri topologique d'un graphe (le cas échéant). BFS peut être utilisé pour trouver le chemin le plus court, avec des limites de poids unitaire, d'un nœud (source d'origine) à un autre. Considérant que, DFS peut être utilisé pour épuiser tous les choix en raison de sa nature d'aller en profondeur, comme la découverte du chemin le plus long entre deux nœuds dans un graphe acyclique. DFS peut également être utilisé pour la détection de cycle dans un graphique.
En fin de compte, si nous avons une profondeur infinie et un facteur de branchement infini, nous pouvons utiliser la recherche par approfondissement itératif (IDS).
Cela dépend de la situation dans laquelle il est utilisé. Chaque fois que nous rencontrons un problème pour parcourir un graphique, nous le faisons dans un but précis. Lorsqu'il existe un problème pour trouver le chemin le plus court dans un graphique non pondéré ou pour déterminer si un graphique est bipartite, nous pouvons utiliser BFS. Pour les problèmes de détection de cycle ou toute logique nécessitant un retour en arrière, nous pouvons utiliser DFS.
Ceci est un bon exemple pour démontrer que BFS est meilleur que DFS dans certains cas. https://leetcode.com/problems/01-matrix/
Lorsqu'elles sont correctement implémentées, les deux solutions doivent visiter les cellules dont la distance est supérieure à celle de la cellule actuelle +1. Mais DFS est inefficace et visite à plusieurs reprises la même cellule, ce qui résulte en une complexité de O (n * n).
Par exemple,
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,