Expliquer Morris aussitôt l'arbre transversal sans l'aide de piles ou de la récursivité
Quelqu'un peut-il m'aider à comprendre les éléments suivants Morris aussitôt l'arbre transversal de l'algorithme sans l'aide de piles ou de la récursivité ? J'essayais de comprendre comment il fonctionne, mais son vient de s'échapper de moi.
1. Initialize current as root
2. While current is not NULL
If current does not have left child
a. Print current’s data
b. Go to the right, i.e., current = current->right
Else
a. In current's left subtree, make current the right child of the rightmost node
b. Go to this left child, i.e., current = current->left
Je comprends l'arbre est modifié de façon que le current node
, est mis à la right child
de la max node
dans right subtree
et utiliser cette propriété pour afinde traversée. Mais au-delà, je suis perdu.
EDIT:
Trouvé cet accompagnement de code c++. J'ai été un moment difficile de comprendre comment l'arbre est restaurée après qu'il est modifié. La magie réside dans la else
clause, qui est touché une fois le droit de la feuille est modifié. Voir le code pour plus de détails:
/* Function to traverse binary tree without recursion and
without stack */
void MorrisTraversal(struct tNode *root)
{
struct tNode *current,*pre;
if(root == NULL)
return;
current = root;
while(current != NULL)
{
if(current->left == NULL)
{
printf(" %d ", current->data);
current = current->right;
}
else
{
/* Find the inorder predecessor of current */
pre = current->left;
while(pre->right != NULL && pre->right != current)
pre = pre->right;
/* Make current as right child of its inorder predecessor */
if(pre->right == NULL)
{
pre->right = current;
current = current->left;
}
//MAGIC OF RESTORING the Tree happens here:
/* Revert the changes made in if part to restore the original
tree i.e., fix the right child of predecssor */
else
{
pre->right = NULL;
printf(" %d ",current->data);
current = current->right;
} /* End of if condition pre->right == NULL */
} /* End of if condition current->left == NULL*/
} /* End of while */
}
- Je n'avais jamais entendu parler de cet algorithme en avant. Très élégant!
- J'ai pensé qu'il pourrait être utile d'indiquer la source de la pseudo-code + code (sans doute).
- source : geeksforgeeks.org/...
Vous devez vous connecter pour publier un commentaire.
Si je suis la lecture de l'algorithme de droite, ce devrait être un exemple de comment cela fonctionne:
D'abord,
X
est la racine, de sorte qu'il est initialisé de lacurrent
.X
a gauche de l'enfant, de sorteX
est faite de l'extrême droite au droit de l'enfant deX
's sous-arbre gauche -- le prédécesseur immédiatX
dans un afinde traversée. DoncX
est fait le droit de l'enfant deB
, puiscurrent
est fixé àY
. L'arbre ressemble maintenant à ceci:(Y)
ci-dessus se réfère àY
et tous ses enfants, qui sont omis pour la récursivité des questions. La partie importante est listé toute façon.Maintenant que l'arbre a un lien de retour vers X, la traversée se poursuit...
Puis
A
est sorti, parce qu'il n'a pas laissé d'enfant, etcurrent
est retourné àY
, qui a été faiteA
du droit de l'enfant à l'itération précédente. Sur la prochaine itération, Y a deux enfants. Cependant, la double condition de la boucle s'arrête quand il atteint lui-même, ce qui est une indication qu'il est sous-arbre gauche a déjà été parcouru. Donc, il imprime lui-même, et se poursuit avec son sous-arbre droit, qui estB
.B
imprime lui-même, et puiscurrent
devientX
, qui passe par le même processus de vérification commeY
ont, aussi réaliser que son sous-arbre gauche a été parcouru, en continuant avec leZ
. Le reste de l'arbre suit le même schéma.Pas de récursivité est nécessaire, parce qu'au lieu de compter sur les retours en arrière par une cheminée, un lien de retour à la racine de la (sous -) arbre est déplacé vers le point où il serait accessible en récursif aussitôt l'arbre transversal de l'algorithme de toute façon, après son sous-arbre gauche est terminé.
Le récursive dans l'ordre de la traversée à l'est :
(in-order(left)->key->in-order(right))
. (ceci est similaire à DFS)Quand nous faisons de la DFS, nous avons besoin de savoir où le retour en arrière (c'est pourquoi nous avons l'habitude de garder une pile).
Que nous allons par le biais d'un nœud parent pour lequel nous aurons besoin de revenir en arrière à l' -> nous de trouver le nœud que nous aurons besoin de revenir en arrière et de mettre à jour son lien avec le nœud parent.
Lorsque nous revenir en arrière? Quand nous ne pouvons pas aller plus loin. Quand nous ne pouvons pas aller plus loin? Quand, ni gauche actuelle de l'enfant.
Où nous revenir? Avis: pour SUCCESSEUR!
Donc, comme nous suivre nœuds le long de la gauche-enfant chemin, mis le prédécesseur à chaque étape pour pointer vers le nœud courant. De cette façon, les prédécesseurs aura des liens vers des successeurs (un lien pour le backtracking).
Nous suivons à gauche alors que nous pouvons jusqu'à ce que nous avons besoin de revenir en arrière. Lorsque nous avons besoin de revenir en arrière, nous imprimons le nœud courant et suivez le lien pour le successeur.
Si nous avons juste fait marche arrière -> il faut suivre le droit des enfants (nous en aurons fini avec la gauche de l'enfant).
Comment savoir si nous avons juste fait marche arrière? Obtenir le prédécesseur de l'actuel nœud et de vérifier si elle a un bon lien (à ce nœud). Si c'est le cas - que nous l'avons suivie. supprimer le lien pour restaurer l'arbre.
Si il n'y avait pas de lien de gauche => nous n'avons pas revenir en arrière et devrait continuer en suivant des enfants à gauche.
Voici mon code Java (Désolé, c'est pas du C++)
Je pense que ce code serait mieux comprendre, il suffit d'utiliser une valeur null pour éviter les boucles infinies, de ne pas avoir à utiliser la magie d'autre. Il peut être facilement adapté à la précommande.
temp.left = null
arbre sera perdu.J'espère que le pseudo-code ci-dessous est plus révélateur:
Se référant au code C++ de la question, l'intérieure de la boucle while trouve les dans l'ordre prédécesseur de l'actuel nœud. Au standard d'un arbre binaire, le droit de l'enfant de son prédécesseur, doit être nulle, alors que dans la version letée le droit de l'enfant doit pointer vers le nœud courant. Si le droit de l'enfant est null, il est défini pour le noeud courant, créant effectivement le filetage, qui est utilisé comme point de retour qui, autrement, auraient à être stocké, généralement sur une pile. Si le droit de l'enfant est pas null, alors l'algorithme permet de s'assurer que l'arbre d'origine est restauré, et puis continue la traversée dans le sous-arbre droit (dans ce cas, il est connu que le sous-arbre gauche a été visité).