web-dev-qa-db-fra.com

Largeur d'abord vs profondeur d'abord

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.

161
Ted Smith

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.

  • Pour profondeur d'abord utilisez une pile. (L'implémentation récursive utilise la pile d'appels ...)
  • Pour width-first utilisez une file d'attente.

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.

271
dmckee

Comprendre les termes:

Cette image devrait vous donner une idée du contexte dans lequel les mots ont une largeur et une profondeur sont utilisés.

Understanding Breadth and Depth


Recherche en profondeur d'abord:

Depth-First Search

  • 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

    1. Si possible, visitez un sommet non visité adjacent, marquez-le comme visité et placez-le sur la pile.
    2. Si vous ne pouvez pas suivre la règle 1, alors, si possible, supprimez un sommet de la pile.
    3. Si vous ne pouvez pas suivre la règle 1 ou la règle 2, vous avez terminé.
  • 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.


Largeur-première recherche:

Breadth-First Search

  • L'algorithme de recherche en largeur d'abord préfère rester le plus près possible du point de départ.
  • Ce type de recherche est généralement implémenté en utilisant un Queue.
  • Règles à suivre: Définir le sommet de départ A comme sommet actuel
    1. Visitez le prochain sommet non visité (s'il en existe un) adjacent au sommet actuel, marquez-le et insérez-le dans la file d'attente.
    2. Si vous ne pouvez pas exécuter la règle 1 car il n’ya plus de sommets non consultés, supprimez un sommet de la file (si possible) et faites-en le sommet actuel.
    3. Si vous ne pouvez pas exécuter la règle 2 parce que la file d'attente est vide, vous avez terminé.
  • 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.

77

Étant donné cet arbre binaire:

enter image description here

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

3
msyinmei

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.

2
user5193682