如何迭代二叉树?

时间:2010-05-31 09:13:40

标签: java algorithm recursion traversal binary-tree

现在我有

 private static void iterateall(BinaryTree foo) {
    if(foo!= null){
    System.out.println(foo.node);
    iterateall(foo.left);
    iterateall(foo.right);
   }
  }

您可以将其更改为迭代而不是递归吗?

6 个答案:

答案 0 :(得分:40)

您正在寻找的是后继算法。

以下是如何定义:

  • 第一条规则:树中的第一个节点是树中最左边的节点。
  • 下一个规则:节点的后继者是:
    • Next-R规则:如果它有一个右子树,则右子树中最左边的节点。
    • Next-U规则:否则,遍历树
      • 如果右转(即此节点是左子节点),则该父节点是继任者
      • 如果你左转(即这个节点是一个正确的孩子),继续上去。
      • 如果你再也不能上去,那就没有继任者了

正如您所看到的,为此,您需要一个父节点指针。


实施例

alt text

  • 第一条规则:树中的第一个节点是树中最左边的节点:(1)
  • Next-U规则:由于(1)没有正确的子树,我们最多可以(3)。这是右转,所以接下来是(3)
  • Next-R规则:由于(3)有一个正确的子树,因此该子树中最左边的节点是下一个:(4)
  • Next-U规则:由于(4)没有正确的子树,我们最多可以(6)。这是右转,接下来是(6)
  • Next-R规则:由于(6)有一个正确的子树,因此该子树中最左边的节点是下一个:(7)
  • Next-U规则:由于(7)没有正确的子树,我们最多可以(6)。这是一个左转,所以我们继续前进到(3)。这是一个左转,所以我们继续前进到(8)。这是右转,接下来是(8)
  • Next-R规则:由于(8)有一个正确的子树,因此该子树中最左边的节点是下一个:(10)
  • Next-R规则:由于(10)有一个正确的子树,因此该子树中最左边的节点是下一个:(13)
  • Next-U规则:由于(13)没有正确的子树,我们最多可以(14)。这是右转,接下来是(14)
  • Next-U规则:由于(14)没有正确的子树,我们最多可以(10)。这是一个左转,所以我们继续前进到(8)。这是一个左转,所以我们想继续上升,但由于(8)没有父母,我们已经到了最后。 (14)没有继任者。

伪代码

Node getLeftMost(Node n)
  WHILE (n.leftChild != NULL)
    n = n.leftChild
  RETURN n

Node getFirst(Tree t)
  IF (t.root == NULL) RETURN NULL
  ELSE
     RETURN getLeftMost(t.root);

Node getNext(Node n)
  IF (n.rightChild != NULL)
     RETURN getLeftMost(n.rightChild)
  ELSE
     WHILE (n.parent != NULL AND n == n.parent.rightChild)
        n = n.parent;
     RETURN n.parent;

PROCEDURE iterateOver(Tree t)
  Node n = getFirst(t);
  WHILE n != NULL
     visit(n)
     n = getNext(n)

Java代码

以上是上述算法的简单实现:

public class SuccessorIteration {
    static class Node {
        final Node left;
        final Node right;
        final int key;
        Node parent;
        Node(int key, Node left, Node right) {
            this.key = key;
            this.left = left;
            this.right = right;
            if (left != null) left.parent = this;
            if (right != null) right.parent = this;
        }
        Node getLeftMost() {
            Node n = this;
            while (n.left != null) {
                n = n.left;
            }
            return n;
        }
        Node getNext() {
            if (right != null) {
                return right.getLeftMost();
            } else {
                Node n = this;
                while (n.parent != null && n == n.parent.right) {
                    n = n.parent;
                }
                return n.parent;
            }
        }
    }
}

然后你可以拥有这样的测试工具:

static Node C(int key, Node left, Node right) {
    return new Node(key, left, right);
}
static Node X(int key)             { return C(key, null, null);  }
static Node L(int key, Node left)  { return C(key, left, null);  }
static Node R(int key, Node right) { return C(key, null, right); }
public static void main(String[] args) {
    Node n =
        C(8,
            C(3,
                X(1),
                C(6,
                    X(4),
                    X(7)
                )
            ),
            R(10,
                L(14,
                    X(13)
                )
            )
        );
    Node current = n.getLeftMost();
    while (current != null) {
        System.out.print(current.key + " ");
        current = current.getNext();
    }
}

打印:

1 3 4 6 7 8 10 13 14 

另见

答案 1 :(得分:9)

  

您可以将其更改为迭代而不是递归吗?

您可以使用显式堆栈。伪代码:

private static void iterateall(BinaryTree foo) {
    Stack<BinaryTree> nodes = new Stack<BinaryTree>();
    nodes.push(foo);
    while (!nodes.isEmpty()) {
        BinaryTree node = nodes.pop();
        if (node == null)
            continue;
        System.out.println(node.node);
        nodes.push(node.right);
        nodes.push(node.left);
    }
}

但这并不比递归代码优越(除了代码中缺少的基本条件)。

答案 2 :(得分:2)

当然,您有两种常规算法,depth first searchbreadth first search

如果遍历顺序对您来说并不重要,请先考虑广度,实现迭代更容易。你的算法看起来应该是这样的。

LinkedList queue = new LinkedList();

queue.add(root);

while (!queue.isEmpty()){
    Object element = queue.remove();

    queue.add(element.left);
    queue.add(element.right);

    // Do your processing with element;
}

答案 3 :(得分:1)

与每次递归一样,您可以使用其他数据结构 - 即堆栈。 解决方案的草图

private static void visitall(BinaryTree foo) {
  Stack<BinaryTree> iterationStack = new Stack<BinaryTree>();
  iterationStack.push(foo);

  while (!iterationStack.isEmpty()) {
      BinaryTree current = iterationStack.pop();
      System.out.println(current.node);
      current.push(current.right);        // NOTE! The right one comes first
      current.push(current.left);
   }

}

答案 4 :(得分:0)

是的,您可以将其更改为迭代而不是递归,但随后会变得更加复杂,因为您需要有一些方法来记住从当前节点 back 的去向。在递归的情况下,Java调用堆栈处理它,但在迭代解决方案中,您需要构建自己的堆栈,或者可能在节点中存储指针。

答案 5 :(得分:0)

我有一棵树(不是二进制),最终用这个非常简单的算法解决了它。其他解决方案使用 left right ,这些解决方案与示例中无关或甚至未实现。

我的结构是:每个父节点包含子节点列表的节点,每个子节点包含一个指向父节点的指针。很常见...

经过一系列的重构后,我想出了使用Kotlin的以下示例。转换为您选择的语言应该是微不足道的。

辅助函数

首先,节点必须提供2个简单的功能。这取决于您的Node类的实现:

leftMost - 这是第一个子节点。如果该节点有子节点,则它是第一个子节点等。如果没有子节点,则返回 this

fun leftMost(): Node {
        if (children.isEmpty()) {
            return this
        }
        var n = this
        while (n.children.isNotEmpty()) {
            n = n.children[0]
        }
        return n
}

nextSibling - 此节点的下一个兄弟,或NULL

fun nextSibling(): Node? {
    if (parent == null) return null
    val siblingIndex = parent.children.indexOf(this) + 1
    return if (siblingIndex < parent.children.size) {
        parent.children[siblingIndex]
    } else {
        null
    }
}

迭代

迭代从root的leftMost开始。

然后检查下一个兄弟。

  • 如果NOT NULL是兄弟的leftMostChild
  • 如果是NULL,那么父,如果父是NULL,我们就完成了。

就是这样。

这是一个Kotlin迭代器函数。

fun iterator(): Iterator<Node> {
    var next: Node? = this.leftMost()

    return object : Iterator<Node> {

        override fun hasNext(): Boolean {
            return next != null
        }

        override fun next(): Node {
            val ret = next ?: throw NoSuchElementException()
            next = ret.nextSibling()?.leftMost() ?: ret.parent
            return ret
        }
    }
}

这是相同的next()函数,但没有用于处理NULL值的Kotlin简写,对于那些不熟悉语法的人来说。

fun next(): Node {
    val ret = next
    if (ret == null) throw NoSuchElementException()
    val nextSibling = ret.nextSibling()
    if (nextSibling != null) {
        next = nextSibling.leftMost()
    }
    else {
        next = ret.parent
    }
    return ret
}