web-dev-qa-db-fra.com

Écrire une traversée non récursive d'un arbre de recherche binaire en utilisant un espace constant et O(n) temps d'exécution

Ce n'est pas un devoir, c'est une question d'entrevue.

Le problème ici est que l'algorithme doit être un espace constant ... Je ne sais pas trop comment le faire sans pile, je posterais ce que j'ai écrit avec une pile, mais ce n'est pas pertinent de toute façon.

Voici ce que j'ai essayé: j'ai essayé de faire une traversée de pré-commande et je suis arrivé au nœud le plus à gauche, mais je suis coincé là. Je ne sais pas comment "recurse" sauvegarder sans un pointeur pile/parent.

Toute aide serait appréciée.

(Je le nomme comme Java car c'est ce que je suis à l'aise d'utiliser, mais c'est assez agnostique comme on peut le voir.)

46
user183037

Je n'y ai pas bien réfléchi, mais je pense que c'est possible tant que vous êtes prêt à tout gâcher.

Chaque nœud a 2 pointeurs, il peut donc être utilisé pour représenter une liste doublement chaînée. Supposons que vous passiez de Root à Root.Left = Current. Le pointeur Root.Left étant désormais inutile, affectez-le à Current.Right et passez à Current.Left. Au moment où vous atteignez l'enfant le plus à gauche, vous aurez une liste chaînée avec des arbres suspendus à certains nœuds. Maintenant, parcourez cela en répétant le processus pour chaque arbre que vous rencontrez au fur et à mesure

EDIT: réfléchi. Voici l'algorithme qui imprime dans l'ordre:

void traverse (Node root) {
  traverse (root.left, root);
}

void traverse (Node current, Node parent) {
  while (current != null) {
    if (parent != null) {
      parent.left = current.right;
      current.right = parent;
    }

    if (current.left != null) {
      parent = current;
      current = current.left;
    } else {
      print(current);
      current = current.right;
      parent = null;
    }
  }
}
29
iluxa

Qu'en est-il de la traversée d'arbres Morris Inorder? Il est basé sur la notion d’arbres threadés et modifie l’arbre, mais le rétablit quand il est terminé.

Linkie: http://geeksforgeeks.org/?p=6358

N'utilise pas d'espace supplémentaire.

27
brainydexter

Si vous utilisez une arborescence basée sur un pointeur descendant et que vous n'avez pas de pointeur parent ou une autre mémoire, il est impossible de le parcourir dans un espace constant. 

Cela est possible si votre arbre binaire est dans un tableau au lieu d'une structure d'objet basée sur un pointeur. Mais alors vous pouvez accéder directement à n’importe quel nœud. Ce qui est une sorte de tricherie ;-)

4
jmg

Voici une version abrégée de réponse originale ..__ de iluxa. Il exécute exactement les mêmes étapes de manipulation et d'impression des nœuds, dans le même ordre, mais de manière simplifiée [1]:

void traverse (Node n) {
  while (n) {
    Node next = n.left;
    if (next) {
      n.left = next.right;
      next.right = n;
      n = next;
    } else {
      print(n);
      n = n.right;
    }
  }
}

[1] De plus, cela fonctionne même lorsque le nœud racine de l’arbre n’a pas d’enfant de gauche.

3
ens

Le titre de la question ne mentionne pas l'absence de pointeur "parent" dans le nœud. Bien que cela ne soit pas nécessairement requis pour BST, de nombreuses implémentations d'arborescence binaire ont un pointeur parent . Classe Node { Nœud * à gauche; Node * right; Nœud * parent; DONNÉES données; };

Si tel est le cas, créez un diagramme de l’arbre sur du papier et dessinez avec un crayon autour de l’arbre, en montant et en descendant des deux côtés des bords (en descendant, vous resterez à gauche du bord, et en montant, vous serez du côté droit). Fondamentalement, il y a 4 états:

  1. SouthWest: Vous êtes sur le côté gauche de Edge, allant du parent à son enfant gauche
  2. NorthEast: aller d'un enfant gauche à son parent
  3. SouthEast: passer d'un parent à un enfant juste
  4. Nord-Ouest: passer d'un enfant droit à son parent

Traverse( Node* node )
{
    enum DIRECTION {SW, NE, SE, NW};
    DIRECTION direction=SW;

    while( node )
    {
        // first, output the node data, if I'm on my way down:
        if( direction==SE or direction==SW ) {
            out_stream << node->data;
        }

        switch( direction ) {
        case SW:                
            if( node->left ) {
                // if we have a left child, keep going down left
                node = node->left;
            }
            else if( node->right ) {
                // we don't have a left child, go right
                node = node->right;
                DIRECTION = SE;
            }
            else {
                // no children, go up.
                DIRECTION = NE;
            }
            break;
        case SE:
            if( node->left ) {
                DIRECTION = SW;
                node = node->left;
            }
            else if( node->right ) {
                node = node->right;
            }
            else {
                DIRECTION = NW;
            }
            break;
        case NE:
            if( node->right ) {
                // take a u-turn back to the right node
                node = node->right;
                DIRECTION = SE;
            }
            else {
                node = node->parent;
            }
            break;
        case NW:
            node = node->parent;
            break;
        }
    }
}
0
Uri

C'est un arbre de recherche, vous pouvez donc toujours obtenir la prochaine clé/entrée

Vous avez besoin de quelque chose comme (je n'ai pas testé le code, mais c'est aussi simple que cela)

Java.util.NavigableMap<K, V> map=...
for (Entry<K, V> e = map.firstEntry(); e!=null; e = map.higherEntry(e.getKey())) {
  process(e)
}

pour plus de clarté, c'est higherEntry, donc ce n'est pas récursif. Voilà :)

final Entry<K,V> getHigherEntry(K key) {
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = compare(key, p.key);
        if (cmp < 0) {
            if (p.left != null)
                p = p.left;
            else
                return p;
        } else {
            if (p.right != null) {
                p = p.right;
            } else {
                Entry<K,V> parent = p.parent;
                Entry<K,V> ch = p;
                while (parent != null && ch == parent.right) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        }
    }
    return null;
}
0
bestsss

La réponse acceptée nécessite la modification suivante, sinon elle n'imprimera pas l'arborescence dans laquelle le fichier BST ne comporte qu'un seul nœud.

if (current == NULL && root != NULL)
   print(root);
0
Steffen San

cas spécial mineur pour la réponse iluxa ci-dessus

if(current== null)
        {
            current = root;
            parent = current.Right;
            if(parent != null)
            {
                current.Right = parent.Left;
                parent.Left = current;
            }
        }
0
Somak Chattopadhyay