Помогите мне понять обход Inorder без использования рекурсии
Я могу понять предварительный обход без использования рекурсии, но мне трудно с обход inorder. Я просто, кажется, не понимаю этого, потому что я не понял внутреннюю работу рекурсии.
это то, что я пытался до сих пор:
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
внутренний while-loop просто не чувствует себя правильно. Кроме того, некоторые из элементов печатаются дважды; может быть, я могу решить это, проверив, был ли этот узел напечатан раньше, но для этого требуется еще одна переменная, которая, опять же, кажется неправильной. Где я ошибаюсь?
Я не пробовал postorder traversal, но я думаю, что это похоже, и я столкнусь с той же концептуальной блокировкой там тоже.
Спасибо за ваше время!
С. П.: определения Lifo и 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
            14 ответов
начните с рекурсивного алгоритма (псевдокода):
traverse(node):
  if node != None do:
    traverse(node.left)
    print node.value
    traverse(node.right)
  endif
Это явный случай хвостовой рекурсии, так что вы можете легко превратить его в цикл while.
traverse(node):
  while node != None do:
    traverse(node.left)
    print node.value
    node = node.right
  endwhile
вы остаетесь с рекурсивным вызовом. Рекурсивный вызов нажимает новый контекст в стеке, запускает код с самого начала, затем извлекает контекст и продолжает делать то, что он делал. Итак, вы создаете стек для хранения и цикл, который определяет на каждой итерации, находимся ли мы в ситуация" first run "(ненулевой узел) или ситуация "returning" (нулевой узел, непустой стек) и запускает соответствующий код:
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
трудная вещь, котор нужно схватить часть "Возвращения": вы должны определить, в вашем цикле, ли код вы бежите в ситуации" входя в функцию "или в ситуации" возвращающ от звонока", и вы будете иметь if/else сеть с таким количеством случаев, как у вас нетерминальные рекурсии в коде.
в данном конкретном ситуация, мы используем узел для хранения информации о ситуации. Другим способом было бы сохранить это в самом стеке (так же, как компьютер делает для рекурсии). С помощью этой техники код менее оптимален, но легче следовать
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 
вот простой по порядку нерекурсивный код c++..
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;
        }
    }
}
def print_tree_in(root):
    stack = []
    current = root
    while True:
        while current is not None:
            stack.append(current)
            current = current.getLeft();
        if not stack:
            return
        current = stack.pop()
        print current.getValue()
        while current.getRight is None and stack:
            current = stack.pop()
            print current.getValue()
        current = current.getRight();
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: Я не знаю Python, поэтому может быть несколько проблем с синтаксисом.
вот пример обхода порядка с использованием стека в c# (.net):
(для итеративного почтового заказа вы можете обратиться к:обход порядка Post двоичного дерева без рекурсии)
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);
        }
вот образец с посещенным флагом:
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);
        }
определения утилиты 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;        
    }
состояние можно запомнить неявно,
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*/
      }
    }
 }
@Victor, у меня есть некоторые предложения по вашей реализации, пытающейся протолкнуть состояние в стек. Не вижу в этом необходимости. Потому что каждый элемент, который вы берете из стека, уже пройден. поэтому вместо того, чтобы хранить информацию в стеке, нам нужен флаг, указывающий, является ли следующий обрабатываемый узел из этого стека или нет. Ниже моя реализация, которая отлично работает:
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
небольшая оптимизация ответа @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
Это может быть полезно (реализация на 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;
        }
    }
}
простой итеративный обход порядка без рекурсии
'''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)
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
вот итеративное решение C++ в качестве альтернативы тому, что опубликовал @Emadpres:
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);
    }
}
вот итеративный код Python для обхода Inorder::
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)
Я думаю, что часть проблемы заключается в использовании переменной "prev". Вам не нужно хранить предыдущий узел, вы должны иметь возможность поддерживать состояние в самом стеке (Lifo).
С Википедия, алгоритм, к которому вы стремитесь, это:
- посетить корень.
 - обход левого поддерева
 - пройти правое поддерево
 
в псевдо-коде (отказ от ответственности, я не знаю Python, поэтому извиняюсь за Код стиля Python / C++ ниже!) ваш алгоритм будет чем-то вроде:
lifo = Lifo();
lifo.push(rootNode);
while(!lifo.empty())
{
    node = lifo.pop();
    if(node is not None)
    {
        print node.value;
        if(node.right is not None)
        {
            lifo.push(node.right);
        }
        if(node.left is not None)
        {
            lifo.push(node.left);
        }
    }
}
для обхода postorder вы просто меняете порядок, который вы нажимаете на левый и правый поддеревья в стек.