Lorsque vous parcourez un arbre/un graphique, quelle est la différence entre la largeur d'abord et la profondeur d'abord? N'importe quel exemple de codage ou de pseudocode serait génial.
Ces deux termes différencient deux manières différentes de marcher dans un arbre.
Il est probablement plus facile de simplement montrer la différence. Considérez l'arbre:
A
/ \
B C
/ / \
D E F
Un profondeur première traversée visiterait les nœuds dans cet ordre
A, B, D, C, E, F
Remarquez que vous allez jusqu'au bout en bas une jambe avant de continuer.
Un largeur la première traversée visiterait le noeud dans cet ordre
A, B, C, D, E, F
Ici, nous travaillons à fond à travers chaque niveau avant de descendre.
(Notez qu’il ya une certaine ambiguïté dans les ordres de parcours, et j’ai triché pour maintenir l’ordre de "lecture" à chaque niveau de l’arbre. Dans les deux cas, je pouvais arriver à B avant ou après C, et de même à E avant ou après F. Cela peut être important ou non, cela dépend de votre application ...)
Les deux types de parcours peuvent être réalisés avec le pseudocode:
Store the root node in Container
While (there are nodes in Container)
N = Get the "next" node from Container
Store all the children of N in Container
Do some work on N
La différence entre les deux ordres de traversée réside dans le choix de Container
.
L'implémentation récursive ressemble à
ProcessNode(Node)
Work on the payload Node
Foreach child of Node
ProcessNode(child)
/* Alternate time to work on the payload Node (see below) */
La récursivité se termine lorsque vous atteignez un nœud qui n'a pas d'enfants. Elle se termine donc pour les graphes finis et acycliques.
A ce stade, j'ai encore un peu triché. Avec un peu d'intelligence, vous pouvez également travailler les nœuds dans cet ordre:
D, B, E, F, C, A
ce qui est une variation de la profondeur d'abord, où je ne fais pas le travail à chaque nœud avant de remonter dans l'arbre. J'ai cependant visité les nœuds supérieurs en descendant pour retrouver leurs enfants.
Cette traversée est assez naturelle dans l'implémentation récursive (utilisez la ligne "Heure alternative" ci-dessus au lieu de la première ligne "Travail"), et pas trop difficile si vous utilisez une pile explicite, mais que je le laisse comme exercice.
Cette image devrait vous donner une idée du contexte dans lequel les mots ont une largeur et une profondeur sont utilisés.
L'algorithme de recherche d'abord en profondeur agit comme s'il souhaitait s'éloigner le plus rapidement possible du point de départ.
Il utilise généralement un Stack
pour se rappeler où il devrait aller quand il atteint une impasse.
Règles à suivre: Poussez le premier sommet A sur le Stack
Code Java:
public void searchDepthFirst() {
// Begin at vertex 0 (A)
vertexList[0].wasVisited = true;
displayVertex(0);
stack.Push(0);
while (!stack.isEmpty()) {
int adjacentVertex = getAdjacentUnvisitedVertex(stack.peek());
// If no such vertex
if (adjacentVertex == -1) {
stack.pop();
} else {
vertexList[adjacentVertex].wasVisited = true;
// Do something
stack.Push(adjacentVertex);
}
}
// Stack is empty, so we're done, reset flags
for (int j = 0; j < nVerts; j++)
vertexList[j].wasVisited = false;
}
Applications : 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.
Queue
.Code Java:
public void searchBreadthFirst() {
vertexList[0].wasVisited = true;
displayVertex(0);
queue.insert(0);
int v2;
while (!queue.isEmpty()) {
int v1 = queue.remove();
// Until it has no unvisited neighbors, get one
while ((v2 = getAdjUnvisitedVertex(v1)) != -1) {
vertexList[v2].wasVisited = true;
// Do something
queue.insert(v2);
}
}
// Queue is empty, so we're done, reset flags
for (int j = 0; j < nVerts; j++)
vertexList[j].wasVisited = false;
}
Applications : La recherche d'abord par la largeur recherche d'abord tous les sommets situés à un bord du point de départ, puis tous les sommets situés à deux bords, et ainsi de suite. Ceci est utile si vous essayez de trouver le chemin le plus court du sommet au point de départ.
Espérons que cela devrait être suffisant pour comprendre les recherches Largeur-Premier et Profondeur-Premier. Pour des lectures plus poussées, je recommanderais le chapitre Graphiques d’un excellent livre de Robert Lafore sur les structures de données.
Étant donné cet arbre binaire:
Largeur première traversée:
Parcourez chaque niveau de gauche à droite.
"Je suis G, mes enfants sont D et moi, mes petits-enfants sont B, E, H et K, leurs petits-enfants sont A, C, F"
- Level 1: G
- Level 2: D, I
- Level 3: B, E, H, K
- Level 4: A, C, F
Order Searched: G, D, I, B, E, H, K, A, C, F
Profondeur première traversée:
La traversée n'est pas effectuée à TRAVERS des niveaux entiers à la fois. Au lieu de cela, le parcours traverse d'abord la profondeur (de la racine à la feuille) de l'arbre. Cependant, c'est un peu plus complexe que simplement monter et descendre.
Il y a trois méthodes:
1) PREORDER: ROOT, LEFT, RIGHT.
You need to think of this as a recursive process:
Grab the Root. (G)
Then Check the Left. (It's a tree)
Grab the Root of the Left. (D)
Then Check the Left of D. (It's a tree)
Grab the Root of the Left (B)
Then Check the Left of B. (A)
Check the Right of B. (C, and it's a leaf node. Finish B tree. Continue D tree)
Check the Right of D. (It's a tree)
Grab the Root. (E)
Check the Left of E. (Nothing)
Check the Right of E. (F, Finish D Tree. Move back to G Tree)
Check the Right of G. (It's a tree)
Grab the Root of I Tree. (I)
Check the Left. (H, it's a leaf.)
Check the Right. (K, it's a leaf. Finish G tree)
DONE: G, D, B, A, C, E, F, I, H, K
2) INORDER: LEFT, ROOT, RIGHT
Where the root is "in" or between the left and right child node.
Check the Left of the G Tree. (It's a D Tree)
Check the Left of the D Tree. (It's a B Tree)
Check the Left of the B Tree. (A)
Check the Root of the B Tree (B)
Check the Right of the B Tree (C, finished B Tree!)
Check the Right of the D Tree (It's a E Tree)
Check the Left of the E Tree. (Nothing)
Check the Right of the E Tree. (F, it's a leaf. Finish E Tree. Finish D Tree)...
Onwards until...
DONE: A, B, C, D, E, F, G, H, I, K
3) POSTORDER:
LEFT, RIGHT, ROOT
DONE: A, C, B, F, E, D, H, K, I, G
Utilisation (alias, pourquoi nous en soucions-nous):
J'ai vraiment apprécié cette explication simple de Quora sur les méthodes Depth First Traversal et leur utilisation courante:
"In-Order Traversal imprimera les valeurs [dans l’ordre du BST (arbre de recherche binaire)]"
"La traversée de pré-commande est utilisée pour créer une copie de [l'arbre de recherche binaire]."
"La traversée de post-commande est utilisée pour supprimer [l'arbre de recherche binaire]."
https://www.quora.com/What-is-the-use-of-pre-order-and-post-order-traversal-of-binary-trees-in-computing
Je pense qu'il serait intéressant d'écrire les deux de manière à ce que seul un changement de lignes de code donne un algorithme ou un autre, afin que vous puissiez voir que votre dillema n'est pas si puissant qu'il semble l'être au premier abord. .
Personnellement, j’aime bien l’interprétation de BFS comme une inondation de paysage: les zones de basse altitude seront inondées en premier, puis les zones de haute altitude suivront. Si vous imaginez les altitudes du paysage comme des isolignes, comme nous le voyons dans les livres de géographie, il est facile de voir que BFS remplit toutes les zones sous la même isoline en même temps, comme ce serait le cas avec la physique. Ainsi, interpréter les altitudes comme distance ou coût réduit donne une idée assez intuitive de l’algorithme.
En gardant cela à l'esprit, vous pouvez facilement adapter l'idée de la recherche en largeur d'abord pour trouver facilement l'arbre de recouvrement minimal, le chemin le plus court et de nombreux autres algorithmes de minimisation.
Je n'ai pas encore vu d'interprétation intuitive de DFS (seulement celle standard sur le labyrinthe, mais elle n'est pas aussi puissante que celle de BFS et les inondations), donc pour moi, il semble que BFS semble mieux corréler avec les phénomènes physiques décrits ci-dessus, alors que DFS est plus en corrélation avec les choix de dillema sur des systèmes rationnels (c’est-à-dire que ce sont les personnes ou les ordinateurs qui décident de ce qu’il faut faire pour une partie d’échecs ou pour sortir d’un labyrinthe).
Donc, pour moi, la différence entre les mensonges sur lesquels le phénomène naturel correspond le mieux à son modèle de propagation (transversal) dans la vie réelle.