维基百科迭代后序树遍历伪代码错了吗?

时间:2014-11-14 22:08:06

标签: algorithm tree tree-traversal postorder

这是维基百科为迭代后序树遍历提供的伪代码。

iterativePostorder(node)
  parentStack = empty stack  
  lastnodevisited = null 
  while (not parentStack.isEmpty() or node ≠ null)
    if (node ≠ null)
      parentStack.push(node)
      node = node.left
    else
      peeknode = parentStack.peek()
      if (peeknode.right ≠ null and lastnodevisited ≠ peeknode.right) 
        /* if right child exists AND traversing node from left child, move right */
        node = peeknode.right
      else
        visit(peeknode)
        lastnodevisited = parentStack.pop() 

非常简单,我已经用Java实现了它。但它不起作用,问题是每次访问最左边的叶子并返回其父叶子时,它会在下一次迭代中将左叶子再次添加到堆栈中。这会导致无限循环。我的方法不正确还是维基百科版本错了?

public static List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<Integer>();
    if (root == null) return res;
    Stack<TreeNode> s = new Stack<TreeNode>();
    TreeNode lastVisitedNode = null;
    TreeNode curr = root;
    int i = 0;
    while (curr != null || !s.isEmpty()) {
        if (curr != null) {
            System.out.println("push " + curr.val);
            s.push(curr);
            curr = curr.left;
        } else {
            curr = s.peek();
            if (curr.right != null && lastVisitedNode != curr.right) {
                curr = curr.right;
            } else {
                res.add(curr.val);
                System.out.println("pop " + curr.val);
                lastVisitedNode = s.pop();
            }
        }
        System.out.println(s);
        System.out.println(res);
        if (i>8) break;
        else i++;
    }
    return res;
}

2 个答案:

答案 0 :(得分:0)

维基百科版本 错误的原因与您解释的完全相同。

这是一个可能更好的伪代码,来自geeksforgeeks

1.1 Create an empty stack
2.1 Do following while root is not NULL
    a) Push root's right child and then root to stack.
    b) Set root as root's left child.
2.2 Pop an item from stack and set it as root.
    a) If the popped item has a right child and the right child 
       is at top of stack, then remove the right child from stack,
       push the root back and set root as root's right child.
    b) Else print root's data and set root as NULL.
2.3 Repeat steps 2.1 and 2.2 while stack is not empty.

您必须添加额外的代码来检查2.1.a中的节点右子节点是否为空。

答案 1 :(得分:0)

wikipedia伪代码没有错。他们使用两个不同的变量:nodepeekNode,而您只使用currNode == null指的是没有剩下左侧分支要探索的情况,因此我们可以停止推送,而是调查堆栈中的下一个元素。您可以恢复使用两个不同的变量,也可以在代码中进行以下修复:

由于每次调查堆栈时都会将curr重新分配给非空值,因此在访问节点后需要将curr重置为null。 (因为仍然没有留下分支的状态仍未改变)。

wikipedia伪代码不必执行此操作,因为它们的node值仍为空。

这是我的代码,它提供了一个完美的答案:

var currentNode = this.root()
var previousNode = null
while(!nodeStack.isEmpty() || currentNode) {

    // If there is a node on which the recursive call is made, we have a subtree to explore. If this is null, we have to backtrack and do a callback. 
    if (currentNode) {
        nodeStack.push(currentNode)
        previousNode = currentNode
        currentNode = currentNode.leftChild
    } else {
        currentNode = nodeStack.peek()
        if (currentNode.rightChild && previousNode != currentNode.rightChild) {
            currentNode = currentNode.rightChild    
        } else {
            callback(currentNode)
            currentNode = null
            previousNode = nodeStack.pop()
        }
    } 
}