web-dev-qa-db-fra.com

Aidez-moi à comprendre Inverse Traversal sans utiliser la récursivité

Je suis capable de comprendre la traversée de pré-commande sans utiliser la récursivité, mais j'ai de la difficulté à traverser en ordre. Je n'arrive pas à comprendre, peut-être parce que je n'ai pas compris le fonctionnement interne de la récursion.

C'est ce que j'ai essayé jusqu'à présent:

def traverseInorder(node):
    lifo = Lifo()
    lifo.Push(node)
    while True:
        if node is None:
            break
        if node.left is not None:
            lifo.Push(node.left)
            node = node.left
            continue
        prev = node
        while True:
            if node is None:
                break
            print node.value
            prev = node
            node = lifo.pop()
        node = prev
        if node.right is not None:
            lifo.Push(node.right)
            node = node.right
        else:
            break

La boucle intérieure tout ne se sent pas bien. De plus, certains éléments sont imprimés deux fois. peut-être que je peux résoudre ce problème en vérifiant si ce nœud a déjà été imprimé, mais cela nécessite une autre variable qui, encore une fois, ne semble pas appropriée. Où vais-je mal?

Je n'ai pas essayé la traversée post-commande, mais je suppose que c'est similaire et que je ferai face au même blocage conceptuel là aussi.

Merci pour votre temps!

P.S .: Définitions de Lifo et Node:

class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

class Lifo:
    def __init__(self):
        self.lifo = ()
    def Push(self, data):
        self.lifo = (data, self.lifo)
    def pop(self):
        if len(self.lifo) == 0:
            return None
        ret, self.lifo = self.lifo
        return ret
31
Srikanth

Commencez avec l'algorithme récursif (pseudocode): 

traverse(node):
  if node != None do:
    traverse(node.left)
    print node.value
    traverse(node.right)
  endif

Il s'agit clairement d'un cas de récursion de la queue, vous pouvez donc facilement le transformer en boucle while. 

traverse(node):
  while node != None do:
    traverse(node.left)
    print node.value
    node = node.right
  endwhile

Vous êtes laissé avec un appel récursif. Ce que fait l'appel récursif, c'est pousser un nouveau contexte sur la pile, exécuter le code depuis le début, puis récupérer le contexte et continuer à faire ce qu'il faisait. Donc, vous créez une pile pour le stockage et une boucle qui détermine, à chaque itération, si nous sommes dans une situation de "première exécution" (nœud non nul) ou dans une situation de "renvoi" (nœud nul, pile non vide ) et exécute le code approprié: 

traverse(node):
  stack = []
  while !empty(stack) || node != None do:
    if node != None do: // this is a normal call, recurse
      Push(stack,node)
      node = node.left
    else // we are now returning: pop and print the current node
      node = pop(stack)
      print node.value
      node = node.right
    endif
  endwhile

La partie difficile à saisir est la partie "retour": vous devez déterminer, dans votre boucle, si le code que vous utilisez est dans la situation "entrer dans la fonction" ou dans la situation "revenir d'un appel", et vous aura une chaîne if/else avec autant de cas que vous avez de récursions non terminales dans votre code.

Dans cette situation spécifique, nous utilisons le nœud pour conserver des informations sur la situation. Une autre façon serait de stocker cela dans la pile elle-même (comme le fait un ordinateur pour la récursion). Avec cette technique, le code est moins optimal mais plus facile à suivre

traverse(node):
  // entry:
  if node == NULL do return
  traverse(node.left)
  // after-left-traversal:
  print node.value
  traverse(node.right)

traverse(node):
   stack = [node,'entry']
   while !empty(stack) do:
     [node,state] = pop(stack)
     switch state: 
       case 'entry': 
         if node == None do: break; // return
         Push(stack,[node,'after-left-traversal']) // store return address
         Push(stack,[node.left,'entry']) // recursive call
         break;
       case 'after-left-traversal': 
         print node.value;
         // tail call : no return address
         Push(stack,[node.right,'entry']) // recursive call
      end
    endwhile 
80
Victor Nicollet

Voici un code c ++ simple et non récursif dans l'ordre.

void inorder (node *n)
{
    stack s;

    while(n){
        s.Push(n);
        n=n->left;
    }

    while(!s.empty()){
        node *t=s.pop();
        cout<<t->data;
        t=t->right;

        while(t){
            s.Push(t);
            t = t->left;
        }
    }
}
14
Emadpres
 def print_tree_in (racine): 
 pile = [] 
 actuelle = racine 
 tant que True: 
 tant que la valeur actuelle n'est pas Aucune: 
 stack.append (current) 
 current = current.getLeft (); 
 si non stack: 
 renvoyer 
 current = stack.pop () 
 print current.getValue () 
 alors que current.getRight vaut Aucun et empiler: 
 current = stack.pop () 
 print current.getValue () [. ] current = current.getRight (); 
2
Ashish

Voici un exemple de traversée en ordre utilisant stack in c # (.net):

(pour post-commande itérative, vous pouvez vous référer à: Traversée de post-commande de l'arbre binaire sans récursion )

public string InOrderIterative()
        {
            List<int> nodes = new List<int>();
            if (null != this._root)
            {
                Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
                var iterativeNode = this._root;
                while(iterativeNode != null)
                {
                    stack.Push(iterativeNode);
                    iterativeNode = iterativeNode.Left;
                }
                while(stack.Count > 0)
                {
                    iterativeNode = stack.Pop();
                    nodes.Add(iterativeNode.Element);
                    if(iterativeNode.Right != null)
                    {
                        stack.Push(iterativeNode.Right);
                        iterativeNode = iterativeNode.Right.Left;
                        while(iterativeNode != null)
                        {
                            stack.Push(iterativeNode);
                            iterativeNode = iterativeNode.Left;
                        }
                    }
                }
            }
            return this.ListToString(nodes);
        }

Voici un exemple avec le drapeau visité:

public string InorderIterative_VisitedFlag()
        {
            List<int> nodes = new List<int>();
            if (null != this._root)
            {
                Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
                BinaryTreeNode iterativeNode = null;
                stack.Push(this._root);
                while(stack.Count > 0)
                {
                    iterativeNode = stack.Pop();
                    if(iterativeNode.visted)
                    {
                        iterativeNode.visted = false;
                        nodes.Add(iterativeNode.Element);
                    }
                    else
                    {
                        iterativeNode.visted = true;
                        if(iterativeNode.Right != null)
                        {
                            stack.Push(iterativeNode.Right);
                        }
                        stack.Push(iterativeNode);
                        if (iterativeNode.Left != null)
                        {
                            stack.Push(iterativeNode.Left);
                        }
                    }
                }
            }
            return this.ListToString(nodes);
        }

les définitions de l'utilitaire binarytreenode, listtostring:

string ListToString(List<int> list)
        {
            string s = string.Join(", ", list);
            return s;
        }


class BinaryTreeNode
    {
        public int Element;
        public BinaryTreeNode Left;
        public BinaryTreeNode Right;        
    }
1
Dreamer
def traverseInorder(node):
   lifo = Lifo()

  while node is not None:
    if node.left is not None:
       lifo.Push(node)
       node = node.left
       continue

   print node.value

   if node.right is not None:
      node = node.right
      continue

   node = lifo.Pop()
   if node is not None :
      print node.value
      node = node.right

PS: Je ne connais pas Python, il peut donc y avoir quelques problèmes de syntaxe.

1
Henk Holterman

Voici une solution C++ itérative comme alternative à ce que @Emadpres a publié:

void inOrderTraversal(Node *n)
{
    stack<Node *> s;
    s.Push(n);
    while (!s.empty()) {
        if (n) {
            n = n->left;
        } else {
            n = s.top(); s.pop();
            cout << n->data << " ";
            n = n->right;
        }
        if (n) s.Push(n);
    }
}

0
jpswain
class Tree:

    def __init__(self, value):
        self.left = None
        self.right = None
        self.value = value

    def insert(self,root,node):
        if root is None:
            root = node
        else:
            if root.value < node.value:
                if root.right is None:
                    root.right = node
                else:
                    self.insert(root.right, node)
            else:
                if root.left is None:
                    root.left = node
                else:
                    self.insert(root.left, node)       

    def inorder(self,tree):
        if tree.left != None:
            self.inorder(tree.left)
        print "value:",tree.value

        if tree.right !=None:
            self.inorder(tree.right)

    def inorderwithoutRecursion(self,tree):
        holdRoot=tree
        temp=holdRoot
        stack=[]
        while temp!=None:
            if temp.left!=None:
                stack.append(temp)
                temp=temp.left
                print "node:left",temp.value

            else:
                if len(stack)>0:
                    temp=stack.pop();
                    temp=temp.right
                    print "node:right",temp.value
0
Siddhartha

Voici un code Python itératif pour Inorder Traversal :: 

class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

def inOrder(root):
    current = root
    s = []
    done = 0

    while(not done):
        if current is not None :
            s.append(current)
            current = current.left
        else :
            if (len(s)>0):
                current = s.pop()
                print current.data
                current = current.right
            else :
                done =1

root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)

inOrder(root)
0
Akash Kandpal

On peut se souvenir implicitement de l'état,

traverse(node) {
   if(!node) return;
   Push(stack, node);
   while (!empty(stack)) {
     /*Remember the left nodes in stack*/
     while (node->left) {
        Push(stack, node->left);
        node = node->left;
      }

      /*Process the node*/
      printf("%d", node->data);

      /*Do the tail recursion*/
      if(node->right) {
         node = node->right
      } else {
         node = pop(stack); /*New Node will be from previous*/
      }
    }
 }
0
user283368

Traversée simple dans l'ordre sans récursion

'''iterative inorder traversal, O(n) time & O(n) space '''

class Node:
    def __init__(self, value, left = None, right = None):
        self.value = value
        self.left = left
        self.right = right

def inorder_iter(root):

    stack = [root]
    current = root

    while len(stack) > 0:
        if current:
            while current.left:
                stack.append(current.left)
                current = current.left
        popped_node = stack.pop()
        current = None
        if popped_node:
            print popped_node.value
            current = popped_node.right
            stack.append(current)

a = Node('a')
b = Node('b')
c = Node('c')
d = Node('d')

b.right = d
a.left = b
a.right = c

inorder_iter(a)
0
yask

Cela peut être utile (implémentation Java)

public void inorderDisplay(Node root) {
    Node current = root;
    LinkedList<Node> stack = new LinkedList<>();
    while (true) {
        if (current != null) {
            stack.Push(current);
            current = current.left;
        } else if (!stack.isEmpty()) {
            current = stack.poll();
            System.out.print(current.data + " ");
            current = current.right;
        } else {
            break;
        }
    }
}
0
Stuck in Java

Petite optimisation de la réponse par @Emadpres

def in_order_search(node):
    stack = Stack()
    current = node

    while True:
        while current is not None:
            stack.Push(current)
            current = current.l_child

        if stack.size() == 0:
            break

        current = stack.pop()
        print(current.data)
        current = current.r_child
0
om471987

@ Victor, j'ai une suggestion sur votre mise en œuvre en essayant de pousser l'état dans la pile. Je ne vois pas que c'est nécessaire. Parce que chaque élément que vous retirez de la pile est déjà laissé traversé. Ainsi, au lieu de stocker les informations dans la pile, tout ce dont nous avons besoin est un indicateur pour indiquer si le prochain nœud à traiter provient de cette pile ou non. Voici ma mise en œuvre qui fonctionne bien:

def intraverse(node):
    stack = []
    leftChecked = False
    while node != None:
        if not leftChecked and node.left != None:
            stack.append(node)
            node = node.left
        else:
            print node.data
            if node.right != None:
                node = node.right
                leftChecked = False
            Elif len(stack)>0:
                node = stack.pop()
                leftChecked = True
            else:
                node = None
0
Leonmax