Non-récursive Depth-First Search (DFS) à l'Aide d'une Pile
Ok c'est mon premier post sur un Débordement de Pile, j'ai lu un peu de temps et vraiment admirer le site. J'espère que c'est quelque chose qui sera acceptable pour les poser. Donc, j'ai entrepris la lecture de l'Intro à l'Algorithmique (Cormen. MIT Press) tout le chemin à travers et je suis prêt à les algorithmes sur les graphes. J'ai étudié la procédure formelle d'algorithmes prévues pour la largeur et la profondeur de recherche dans des détails très fins.
Voici le pseudo-code pour depth-first search:
DFS(G)
-----------------------------------------------------------------------------------
1 for each vertex u ∈ G.V
2 u.color ← WHITE //paint all vertices white; undiscovered
3 u.π ← NIL
4 time ← 0 //global variable, timestamps
5 for each vertex u ∈ G.V
6 if u.color = WHITE
7 DFS-VISIT(G,u)
DFS-VISIT(G, u)
-----------------------------------------------------------------------------------
1 u.color ← GRAY //grey u; it is discovered
2 time ← time + 1
3 u.d ← time
4 for each v ∈ G.Adj[u] //explore edge (u,v)
5 if v.color == WHITE
6 v.π ← u
7 DFS-VISIT(G,v)
8 u.color ← BLACK //blacken u; it is finished
9 time ← time + 1
10 u.f ← time
Cet algorithme est récursif et il produit à partir de sources multiples (découverts tous les sommets disjoints graphique). Il a plusieurs propriétés que la plupart des implémentations spécifiques pourraient quitter. Il distingue entre 3 différentes couleurs des sommets. Au départ, il peint tout en blanc, puis quand ils sont "découverts" (consulté le DFS-VISITE), ils sont ensuite peints en gris. Le DFS-VISITE de l'algorithme exécute une boucle récursive de l'appel lui-même sur la liste d'adjacence de l'actuel sommet et seulement les peintures d'un sommet noir quand il n'a plus de bords pour toute blanche nœud.
Également deux autres attributs de chaque sommet sont maintenus à l'u.d et u.f est le moment de timbres lorsque le sommet a été découvert (peint en gris) ou lorsque l'un des sommets est fini (peint en noir). La première fois qu'un nœud est peint, il a un timbre de temps de l'un, et il est incrémenté de la valeur entière suivante pour chaque fois qu'un autre est peint (qu'il soit gris ou noir). u.π est également entretenu qui est un pointeur vers le nœud à partir duquel u a été découvert.
Algorithm Non-Recursive-DFS(G)
-----------------------------------------------------------------------------------
1 for each vertex u ∈ G.V
2 u.color ← WHITE
3 u.π ← NIL
4 time = 0
5 for each vertex u ∈ G.V
6 if u.color = WHITE
7 u.color ← GRAY
8 time ← time + 1
9 u.d ← time
7 push(u, S)
8 while stack S not empty
9 u ← pop(S)
10 for each vertex v ∈ G.Adj[u]
11 if v.color = WHITE
12 v.color = GRAY
13 time ← time + 1
14 v.d ← time
15 v.π ← u
16 push(v, S)
17 u.color ← BLACK
18 time ← time + 1
19 u.f ← time
* MODIFIER 10/31/12 * C'est embarrassant que mon algorithme a été incorrect pendant si longtemps, il serait de travailler dans la plupart des cas, mais pas tous. J'ai juste une question populaire badge pour la question et j'ai vu où Irfy avait repéré le problème dans sa réponse ci-dessous, c'est donc là que le crédit va. Je suis juste de le fixer ici pour quelqu'un qui a besoin de cela dans l'avenir.
Personne ne voir une faille avec l'algorithme ci-dessus? Je suis de loin pas un expert en théorie des graphes, mais je pense que j'ai une assez bonne emprise sur la récursivité et itération et je crois que cela devrait fonctionner de la même. Je suis à la recherche pour le rendre fonctionnellement équivalent à l'algorithme récursif. Il doit conserver tous les attributs du premier algorithme, même si elles ne sont pas nécessaires.
Quand j'ai commencé à l'écrire, je ne pense pas que j'aurais un triplement des boucles, mais c'est la façon dont il s'est avéré. J'ai vu d'autres algorithmes itératifs quand j'ai regardé autour de Google, qui ne disposent que d'une double boucle imbriquée, cependant, ils ne semblent pas procéder à partir de plusieurs sources. (c'est à dire qu'ils ne seront pas de découvrir à chaque vertex sans lien graphique)
if
énoncé dans la deuxième boucle dans le deuxième algorithme. Idéalement également le retrait après la première for
boucleFaites le modifier tout à l'heure, c'est ce que j'avais à l'origine.
Le temps de finition calculée par la non-récursive version sera mauvais. u.f <-temps ne peut être définie qu'après tous les descendants ont leur temps d'arrivée ensemble. Si l'on prend le même exemple en CLRS, puis le nœud u aurait un temps d'arrivée=3, alors que la valeur réelle ont été de 8.
Le calcul des temps de la fin est mauvaise, merci de corriger cela.
PLC déclare que chaque sommet est d'abord blanche jusqu'à ce qu'il est d'abord examiné (découvert) à quel point il est grisé et
u.d
à la nouvelle heure. Il reste gris jusqu'à la liste d'adjacence est examiné complètement (u.d est définie sur tous les nœuds adjacents), à quel point il est blackend et l'heure de fin u.f
à la nouvelle heure.OriginalL'auteur Cory Gross | 2012-04-26
Vous devez vous connecter pour publier un commentaire.
Les deux algorithmes sont très bien. La seconde est une traduction directe de l'récursif à la fonction de pile. Tous la mutation de l'état sont stockées dans la pile.
G
ne change jamais au cours de l'exécution de l'algorithme.Les algorithmes de produire un spanning tree pour chaque déconnecté de la région, basé sur l'ordre de l'algorithme visité chaque nœud. Les arbres seront représentées à la fois les références à la mère-nœuds (
u.π
), et que le segment des arbres (u.d
etu.f
).De l'enfant est une référence à son nœud parent (ou
NULL
si c'est une racine), ainsi que d'avoir la gamme (child.d .. child.f
) contenue à l'intérieur de son parent.Edit: j'ai trouvé une erreur dans la traduction. Vous n'êtes pas réellement pousse l'état actuel dans la pile, mais un avenir argument de méthode. En outre, vous n'êtes pas la coloration de l'sauté nœuds
GRAY
et le réglage de laf
champ.Ici est une réécriture de l'origine première de l'algorithme:
Il y a probablement quelques endroits qui pourraient être optimisés, mais devrait au moins fonctionner maintenant.
Résultat:
Markus, vous avez un très bon point en reconnaissant le segment des arbres, ce qui m'a conduit à réaliser l'algorithme est incorrect. Voir mon post ci-dessus.
OriginalL'auteur Markus Jarderot
D'accord avec @Accroché, il ne semble pas que l'auteur de la question demande une mise en œuvre. Si vous pensez qu'il peut aider, ce serait sympa d'avoir un texte expliquant comment exactement.
OriginalL'auteur hemant
Je pense que j'ai réussi à écrire un beaucoup plus simple de pseudo-code.
mais d'abord, quelques remarques à faire des choses un peu clair:
L'algorithme est basé sur l'observation suivante:
un sommet est poussé sur la pile lors de la visite. et supprimés (sauté) seulement lorsque nous aurons terminé l'examen (noircissement) de toutes ses descendants.
OriginalL'auteur
Voici le code en C++.
Simple itératif DFS. Vous pouvez également voir les découvrir le temps et les temps de la fin. Des doutes s'il vous plaît commentaire. J'ai inclus suffisamment de commentaires pour comprendre le code.
OriginalL'auteur Rusty
Vous avez un grave défaut dans votre non-récursive code.
Après vous vérifiez si
v
estWHITE
, vous ne marquera jamais ilGRAY
avant de le pousser, de sorte qu'il peut être considéré commeWHITE
encore et encore, d'autres non visités nœuds, résultant de multiples références à lav
nœud poussé à la pile. C'est potentiellement catastrophique faille (pouvant provoquer une boucle infinie ou tel).Aussi, vous ne définissez pas
.d
sauf pour les nœuds racine. Cela signifie que le Ensemble imbriqué modèle attributs.d
s et.f
s n'est pas correct. (Si vous ne savez pas ce.d
s et.f
s sont, à la lecture de cet article, il a été très instructif pour moi à l'époque. L'articleleft
est votre.d
etright
est votre.f
.)Votre intérieur
if
fondamentalement doit être le même que l'extérieurif
moins les boucles, plus le parent de référence. Qui est:Les corriger, et ce doit être un vrai équivalent.
J'ai ajouté une image à l'original post qui explique comment u.d et u.f sera attribué.
Mis à jour avec de très, très différentes informations à partir de l'original post. 🙂
Le
f
domaine de la société mère sera mis à l'époque, juste après la poussant tous les enfants, pas après avoir fait la visite. Le parent doit envelopper la start-temps de ses enfants, mais pas de l'ensemble de leurs sous-arbres.Je viens de voir votre mise à jour ici, lorsque j'ai eu ma question populaire badge et j'ai corrigé mon post original, merci beaucoup.
OriginalL'auteur Irfy
Dans le non-récursive version nous avons besoin d'une autre couleur qui reflète l'état de la pile récursive. Donc, nous allons ajouter une couleur=ROUGE pour indiquer que tous les enfants du nœud ont été poussés sur la pile. Je vais aussi supposer que la pile a un peek (), méthode(qui, autrement, pourraient être simulé avec de la pop et immédiate de la poussée)
Donc, avec cet ajout à la version mise à jour de l'original post devrait ressembler à:
OriginalL'auteur Guru Devanla
OriginalL'auteur noDispName
Je crois qu'il y a au moins un cas où la récursivité et pile de versions ne sont pas fonctionnellement équivalent. Prenons le cas d'un Triangle de sommets A, B, et C reliés les uns aux autres. Maintenant, avec le Récursive DFS, le prédécesseur graphique que l'on pourrait obtenir avec Une source, serait soit Un->B->C->C->B ( A->B implique A est le parent de B en profondeur d'abord de l'arbre).
Toutefois, si vous utilisez la pile de la version de la dsv, de parents de B et C, serait toujours enregistrés comme A. Il ne peut jamais être le cas que parent de B est C ou vice-versa (ce qui est toujours le cas pour les DFS). C'est parce que, lors de l'exploration de la liste d'adjacence d'un sommet quelconque (ici), nous poussons tous les membres de la contiguïté de la liste (ici B et C) dans un aller.
Cela peut devenir pertinent, si vous essayez d'utiliser DFS pour trouver l'articulation des points dans un graphe[1].
Un exemple serait que l'énoncé suivant est vrai que si nous utilisons une version récursive de la dsv.
Dans un triangle, il n'y a évidemment pas de point d'articulation, mais la pile-DFS donne encore deux enfants à n'importe quelle source sommet dans la profondeur d'abord de l'arbre (Un a des enfants, B et C). C'est seulement si l'on crée de la profondeur d'abord de l'arbre à l'aide de récursive DFS que la déclaration ci-dessus est vrai.
[1] Introduction aux Algorithmes, PLC - Problème 22-2 (Deuxième et la Troisième Édition)
Mon point est que la différence se pose en raison de l'implémentation. Comme je l'ai dit, dans la pile version nous pousser tous les voisins d'un sommet en une seule fois contrairement à la récursif cas, où nous visite chaque nœud voisin de manière récursive. Je ne suis pas allé à travers ce lien à fond, mais on dirait qu'il traite de la même question. jroller.com/bobfoster/entry/depth_first_search_with_explicit
OriginalL'auteur gjain