Mise en œuvre d'un itérateur sur un arbre de recherche binaire
J'ai été le codage d'un tas de différents binaires de recherche arbres implémentations récemment (AVL, s'écartent, treap) et je suis curieux de savoir si il y a notamment un "bonne" façon d'écrire un itérateur pour parcourir ces structures. La solution que j'ai utilisé en ce moment, chaque nœud dans le BST stocker des pointeurs vers le suivant et précédent éléments dans l'arbre, ce qui réduit l'itération à une norme de liste liée itération. Cependant, je ne suis pas vraiment satisfait de cette réponse. Il augmente l'utilisation de l'espace de chaque nœud par deux pointeurs (précédent et suivant), et dans un certain sens, c'est juste de la triche.
Je sais que d'une façon de construire un arbre de recherche binaire itérateur qui utilise O(h) auxiliaire de l'espace de stockage (où h est la hauteur de l'arbre) à l'aide d'une pile de garder une trace de la frontière nœuds à explorer plus tard, mais j'ai résisté à ce codage a cause de l'utilisation de la mémoire. J'espérais qu'il y est une certaine façon de construire un itérateur qui n'utilise que la constante de l'espace.
Ma question est-ce - est-il possible de concevoir un itérateur sur un arbre de recherche binaire avec les propriétés suivantes?
- Éléments sont visités par ordre croissant (c'est à dire un afinde traversal)
next()
ethasNext()
requêtes à exécuter en O(1) fois.- L'utilisation de la mémoire est O(1)
Pour le rendre plus facile, c'est très bien si l'on suppose que la structure de l'arbre n'est pas le changement de forme au cours de l'itération (c'est à dire pas des insertions, des suppressions ou des rotations), mais ce serait vraiment cool si il y avait une solution qui pourrait gérer cela.
- Si la traversée de l'arbre est mutable, vous pouvez utiliser une astuce de TAOCP I. 2.3.1 parcours d'arbres binaires, l'exercice 21. Il prend O(N) et O(1) de la mémoire. Lorsque l'algorithme termine l'arbre de parcours ne sera pas changé. Ce sera la même qu'avant.
- C'est exactement la réponse que je cherche. 🙂
- Pourquoi êtes-vous inquiet à propos de la surcharge de la mémoire de stockage d'une pile de nœuds de l'arborescence dans l'itérateur? C'est seulement O(log n) avec le nombre d'éléments dans l'arbre, si elle est bien équilibrée.
- Je suis en train de maximiser l'asymptotique de la vitesse de copie. À l'aide d'une pile fait itérateur de la copie de O(lg n); j'ai espoir d'obtenir O(1) parce que le C++ itérateurs d'avoir copié et transmis autour d'un lot.
- Henson, le code semble un peu buggé pour moi (je ne suis pas tout à fait sûr, cependant). Dans le BSTIterator<E> & opérateur++() la méthode à, la gauche, la descente doit être itératif, c'est à dire que vous avez à parcourir pour atteindre la gauche-est le nœud de m_curNode->GetRight().
- Votre contribution ici ne permet pas de répondre à la question. Si vous souhaitez ajouter un commentaire, d'augmenter votre notoriété (stackoverflow.com/faq#reputation), si vous avez une question, n'hésitez pas à demander ici: stackoverflow.com/questions/ask
Vous devez vous connecter pour publier un commentaire.
Le plus simple possible itérateur magasins de la dernière vu touche, puis sur la prochaine itération, les recherches de l'arbre pour le moins limite supérieure pour cette clé. L'itération est O(log n). Cela a l'avantage d'être très simple. Si les touches sont petites, puis les itérateurs sont également de petite taille. bien sûr, il a le désavantage d'être relativement lent moyen d'une itération à travers l'arbre. Il aussi de ne pas travailler pour les non-séquences uniques.
Quelques arbres utilisent exactement l'application que vous utilisez déjà, parce que c'est important pour leur utilisation spécifique que la numérisation est très rapide. Si le nombre de clés dans chaque nœud est grand, alors la peine de stockage, de frère, de pointeurs n'est pas trop lourde. La plupart des B-Arbres utilisation de cette méthode.
de nombreuses arbre de recherche implémentations de garder un parent pointeur sur chaque nœud pour simplifier d'autres opérations. Si vous avez, alors vous pouvez utiliser un simple pointeur vers le dernier vu nœud que votre itérateur de l'état. à chaque itération, vous recherchez l'enfant suivant dans le dernier vu du nœud parent. si il n'y a plus de frères et sœurs, puis vous montez encore d'un niveau.
Si aucune de ces techniques ne vous convient pas, vous pouvez utiliser une pile de nœuds, stockées dans l'itérateur. Ce dessert a la même fonction que la pile d'appels de fonction lors du parcours de l'arbre de recherche comme d'habitude, mais au lieu de boucle à travers les frères et sœurs et recursing sur les enfants, vous poussent les enfants dans la pile et le retour de chaque frère ou sœur.
Comme TokenMacGuy mentionné, vous pouvez utiliser une pile stockés dans l'itérateur.
Voici un test rapide de mise en œuvre de ce en Java:
Autre variante serait de parcourir l'arbre au moment de la construction et de sauver la traversée dans une liste. Vous pouvez utiliser l'itérateur liste par la suite.
Ok, je sais que c'est vieux, mais on m'a demandé dans une interview avec Microsoft un temps, et j'ai décidé de travailler un peu. J'ai testé et ça marche assez bien.
L'arbre transversal, depuis Wikipédia:
Dans l'article il y a quelques pseudo-code de l'itération avec O(1) de l'état, qui peut être facilement adapté à un itérateur.
Ce sujet à l'aide d'un parcours en profondeur d'abord technique de recherche. L'itérateur objet doit avoir une pile de nœuds visités.
Si vous utilisez la pile, vous n'réaliser "Supplémentaire de l'utilisation de la mémoire O(h), h est la hauteur de l'arbre".
Cependant, si vous souhaitez utiliser uniquement les O(1) de la mémoire supplémentaire, vous avez besoin d'enregistrer le
Voici l'analyse:
- Si le nœud actuel a la droite de l'enfant: trouver min de sous-arbre droit
- C'nœud actuel n'a pas le droit de l'enfant, vous avez besoin de chercher à partir de la racine, et de garder à jour la plus basse de l'ancêtre, qui est le plus bas de son prochain nœud
Utiliser O(1) de l'espace, ce qui signifie que nous n'allons pas utiliser O(h) de la pile.
Pour commencer:
hasNext()? actuel.val <= endNode.val de vérifier si l'arbre est entièrement parcouru.
Trouver min via la plus à gauche: On peut toujours chercher la plus à gauche pour trouver la valeur minimale.
Une fois de plus à gauche min est cochée (nom, il
current
). Prochaine min 2 cas:Si le courant.droite != nulle, on peut garder à la recherche pour le courant.de la droite la plus à gauche de l'enfant, en tant que proches min.
Ou, nous avons besoin de regarder en arrière pour les parents. Utiliser les binaires un arbre de recherche pour trouver le nœud parent.
Note: lorsque vous effectuez une recherche binaire pour un parent, assurez-vous qu'il répond à un parent.gauche = courant.
Parce que:Si le parent.droit == actuel, ce parent doit avoir été visité avant. Dans l'arbre de recherche binaire,
nous savons que parent.val < parent.la droite.val. Nous avons besoin de sauter ce cas particulier, puisqu'il permet de
pour ifinite boucle.
Par définition, il n'est pas possible pour next() et hasNext() pour exécuter en O(1) fois. Lorsque vous êtes à la recherche à un nœud particulier dans un BST, vous n'avez aucune idée de la hauteur et la structure des autres nœuds sont, par conséquent, vous ne pouvez pas "sauter" à la bonne nœud suivant.
Cependant, l'espace de la complexité peut être réduite à O(1) (sauf pour la mémoire de la BST lui-même). Voici la façon dont je voudrais le faire en C:
Le truc, c'est d'avoir à la fois un lien parent, et visité un drapeau pour chaque nœud. À mon avis, nous pouvons affirmer que ce n'est pas un espace supplémentaire de l'utilisation, il est tout simplement partie de la structure de nœud. Et évidemment, iter_next() doit être appelé sans que l'état de l'arbre de modification de structure (bien sûr), mais aussi que le "visité" les drapeaux de ne pas changer les valeurs.
Ici, c'est le testeur de fonction qui s'appelle iter_next() et imprime la valeur à chaque fois pour cet arbre:
Qui permet d'imprimer les valeurs dans l'ordre de tri: