Supposons que vous vouliez implémenter une recherche en largeur dans un arbre binaire de manière récursive . Comment procéderiez-vous?
Est-il possible d'utiliser uniquement la pile d'appels comme stockage auxiliaire?
Supposons que vous vouliez implémenter une recherche en largeur dans un arbre binaire de manière récursive . Comment procéderiez-vous?
Est-il possible d'utiliser uniquement la pile d'appels comme stockage auxiliaire?
Réponses:
(Je suppose que c'est juste une sorte d'exercice de réflexion, ou même une question astucieuse pour les devoirs / entretien, mais je suppose que je pourrais imaginer un scénario bizarre dans lequel vous n'êtes pas autorisé à tout espace pour une raison quelconque [une très mauvaise coutume gestionnaire de mémoire? quelques problèmes d'exécution / OS bizarres?] pendant que vous avez toujours accès à la pile ...)
La traversée en largeur d'abord utilise traditionnellement une file d'attente, pas une pile. La nature d'une file d'attente et d'une pile est à peu près opposée, donc essayer d'utiliser la pile d'appels (qui est une pile, d'où son nom) comme stockage auxiliaire (une file d'attente) est à peu près voué à l'échec, sauf si vous le faites quelque chose de stupidement ridicule avec la pile d'appels que vous ne devriez pas être.
Dans le même ordre d'idées, la nature de toute récursivité sans queue que vous essayez d'implémenter consiste essentiellement à ajouter une pile à l'algorithme. Cela fait que la première recherche sur un arbre binaire n'est plus étendue, et par conséquent, le temps d'exécution et ainsi de suite pour les BFS traditionnels ne s'appliquent plus complètement. Bien sûr, vous pouvez toujours transformer trivialement n'importe quelle boucle en un appel récursif, mais ce n'est pas une sorte de récursivité significative.
Cependant, il existe des moyens, comme l'ont démontré d'autres, d'implémenter quelque chose qui suit la sémantique de BFS à un certain prix. Si le coût de la comparaison est cher mais que la traversée des nœuds est bon marché, alors comme @Simon Buchan l'a fait, vous pouvez simplement exécuter une recherche itérative en profondeur d'abord, ne traitant que les feuilles. Cela signifierait qu'aucune file d'attente croissante n'est stockée dans le tas, juste une variable de profondeur locale et que les piles sont construites encore et encore sur la pile d'appels au fur et à mesure que l'arbre est parcouru encore et encore. Et comme @Patrick l'a noté, un arbre binaire soutenu par un tableau est généralement stocké dans l'ordre de traversée en largeur d'abord, donc une recherche en largeur d'abord serait triviale, également sans avoir besoin d'une file d'attente auxiliaire.
la source
Si vous utilisez un tableau pour sauvegarder l'arborescence binaire, vous pouvez déterminer algébriquement le nœud suivant. si
i
est un nœud, alors ses enfants peuvent être trouvés à2i + 1
(pour le nœud de gauche) et2i + 2
(pour le nœud de droite). Le prochain voisin d'un nœud est donné pari + 1
, sauf sii
une puissance de2
Voici le pseudocode pour une implémentation très naïve de la première recherche en largeur sur un arbre de recherche binaire basé sur un tableau. Cela suppose un tableau de taille fixe et donc un arbre de profondeur fixe. Il examinera les nœuds sans parents et pourrait créer une pile d'une grande taille impossible à gérer.
la source
Je n'ai pas pu trouver un moyen de le faire complètement récursif (sans aucune structure de données auxiliaire). Mais si la file d'attente Q est passée par référence, vous pouvez avoir la fonction récursive de queue idiote suivante:
la source
La méthode suivante a utilisé un algorithme DFS pour obtenir tous les nœuds dans une profondeur particulière - ce qui revient à faire BFS pour ce niveau. Si vous trouvez la profondeur de l'arbre et faites cela pour tous les niveaux, les résultats seront les mêmes que ceux d'un BFS.
Trouver la profondeur d'un arbre est un jeu d'enfant:
la source
level
soit égal à zéro.Une simple récursivité BFS et DFS en Java: il
suffit de pousser / proposer le nœud racine de l'arborescence dans la pile / file d'attente et d'appeler ces fonctions.
la source
J'ai trouvé un très bel algorithme de traversée récursif (même fonctionnel) lié à la largeur d'abord. Ce n'est pas mon idée, mais je pense que cela devrait être mentionné dans ce sujet.
Chris Okasaki explique très clairement son algorithme de numérotation en largeur de ICFP 2000 à http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html avec seulement 3 images.
L'implémentation Scala de Debasish Ghosh, que j'ai trouvée sur http://debasishg.blogspot.de/2008/09/breadth-first-numbering-okasakis.html , est:
la source
La manière stupide:
la source
Voici une courte solution Scala :
L'idée d' utiliser la valeur de retour comme accumulateur est bien adaptée. Peut être implémenté dans d'autres langages de la même manière, assurez-vous simplement que votre fonction récursive traite la liste des nœuds .
Liste des codes de test (en utilisant l'arbre de test @marco):
Production:
la source
Voici une implémentation python:
la source
Voici une implémentation Scala 2.11.4 de BFS récursif. J'ai sacrifié l'optimisation des appels de fin pour la brièveté, mais la version TCOd est très similaire. Voir aussi la publication de @snv .
la source
Ce qui suit me semble assez naturel, en utilisant Haskell. Itérer de manière récursive sur les niveaux de l'arbre (ici, je rassemble les noms dans une grande chaîne ordonnée pour montrer le chemin à travers l'arbre):
la source
Voici une implémentation Python de traversée récursive BFS, fonctionnant pour un graphe sans cycle.
la source
Je voudrais ajouter mes centimes à la réponse du haut en ce que si le langage prend en charge quelque chose comme générateur, bfs peut être fait de manière co-récursive.
Pour commencer, la réponse de @ Tanzelax se lit comme suit:
En effet, la pile d'un appel de fonction ordinaire ne se comportera pas comme une pile normale. Mais la fonction de générateur suspendra l'exécution de la fonction, ce qui nous donne la possibilité de produire le niveau suivant d'enfants des nœuds sans plonger dans les descendants plus profonds du nœud.
Le code suivant est bfs récursif en Python.
L'intuition ici est:
la source
J'ai dû implémenter une traversée de tas qui sort dans un ordre BFS. Ce n'est pas réellement BFS mais accomplit la même tâche.
la source
Soit v le sommet de départ
Soit G le graphe en question
Ce qui suit est le pseudo code sans utiliser la file d'attente
la source
Le BFS pour un arbre binaire (ou n-aire) peut être effectué de manière récursive sans files d'attente comme suit (ici en Java):
Un exemple de traversée imprimant les numéros 1 à 12 dans l'ordre croissant:
la source
la source
Voici une implémentation JavaScript qui simule une traversée en largeur d'abord avec une récursivité en profondeur d'abord. Je stocke les valeurs de nœud à chaque profondeur dans un tableau, à l'intérieur d'un hachage. Si un niveau existe déjà (nous avons une collision), nous allons donc simplement pousser vers le tableau à ce niveau. Vous pouvez également utiliser un tableau au lieu d'un objet JavaScript puisque nos niveaux sont numériques et peuvent servir d'indices de tableau. Vous pouvez renvoyer des nœuds, des valeurs, les convertir en liste liée ou tout ce que vous voulez. Je renvoie juste des valeurs par souci de simplicité.
Voici un exemple de traversée réelle de largeur en premier utilisant une approche itérative.
la source
Voici mon code pour une implémentation complètement récursive de la recherche en largeur d'abord d'un graphe bidirectionnel sans utiliser de boucle ni de file d'attente.
la source
Implémentation C # d'un algorithme de recherche récursif en largeur d'abord pour un arbre binaire.
Visualisation de données d'arbre binaire
Si vous voulez que l'algorithme fonctionne non seulement avec un arbre binaire, mais avec des graphiques, ce qui peut avoir deux nœuds et plus qui pointent vers le même autre nœud, vous devez éviter l'auto-cycle en tenant la liste des nœuds déjà visités. La mise en œuvre peut ressembler à ceci.
Visualisation des données graphiques
la source
J'ai fait un programme utilisant C ++ qui fonctionne aussi dans le graphe joint et disjoint.
la source