递归是如何遍历树的

时间:2016-10-26 17:56:28

标签: java recursion binary-tree binary-search-tree

java version "1.8.0_92"

我正在研究树以及如何使用递归遍历它们。但我对此感到困惑。

  public void preOrder(BinaryTree root) {
        if(root != null) {
            System.out.println(root);
            preOrder(root.leftChild);  <-- this gets called and will start from the top of the function
            preOrder(root.rightChild); <-- how can this get called if the top one will always calls itself?
        }
    }

我认为第二个preOrder将永远不会被调用,因为上面的调用将始终调用自身,因此第二个将永远不会被执行。

4 个答案:

答案 0 :(得分:1)

总是自己调用。它继续前进,直到它以null leftChild结束。然后执行返回而不做任何事情 - 它备份一个级别并在最低级别父节点的rightChild上重复。当该调用也耗尽了子节点时,执行从处理这个最低级别的父节点返回,再次备份一个调用,并执行节点的rightChild ...直到整个树已经遍历。

答案 1 :(得分:1)

一旦root.leftChild变为空(即,当你到达树的叶子时),preOrder将被调用如下:preOrder(null)。发生这种情况时,条件将评估为false,并且递归退出并停止,此时将评估preOrder(root.rightChild)

这是一个呼叫追踪(在Scala中):

case class BinaryTree(nodeName: String, leftChild: Option[BinaryTree], rightChild: Option[BinaryTree]) {
    def preOrder(root: Option[BinaryTree], depth: Int = 0) {
        root match {
            case Some(root) => {
                println(" " * depth + root.nodeName)
                preOrder(root.leftChild, depth+4)
                preOrder(root.rightChild, depth+4)
            }
            case None => println(" " * depth + "leaf")
        }
    }
}


val tree = new BinaryTree(
    "root", 
    Some(new BinaryTree(
        "a",
         Some(new BinaryTree("aa", None, None)),
         Some(new BinaryTree("ab", None, None)))),
    Some(new BinaryTree(
        "b",
        Some(new BinaryTree("ba", None, None)),
        Some(new BinaryTree("bb", None, None)))))


tree.preOrder(Some(tree))

root
    a
        aa
            leaf
            leaf
        ab
            leaf
            leaf
    b
        ba
            leaf
            leaf
        bb
            leaf
            leaf

答案 2 :(得分:1)

想想它已经打开了一大堆门,直到找到你要找的东西,然后你必须回去关闭/检查其他的。

public void preOrder(BinaryTree root) {
    if(root != null) {
        System.out.println(root);
        preOrder(root.leftChild);  <-- this gets called and will start from the top of the function
        preOrder(root.rightChild); <-- how can this get called if the top one will always calls itself?
    }
}

我们可以使用匹配代码替换preOrder调用,如下所示:

public void preOrder(BinaryTree root) {
    if(root != null) { <-- note the if check
        System.out.println(root);

        if(root.leftChild!= null) { <-- note the if check
            System.out.println(root.leftChild.leftChild);
            preOrder(root.leftChild.leftChild);  <- this expands into another if block
            preOrder(root.leftChild.rightChild); <- this also expands
        }

       if(root.rightChild!= null) { <-- note the if check
            System.out.println(root.rightChild.leftChild);
            preOrder(root.rightChild.leftChild);  
            preOrder(root.rightChild.rightChild);
        }

    }
}

它不断向外扩展...直到你遇到特殊的“基数”,如果条件停止递归。在这种情况下,当您找到树的叶节点,即node == null时,它会停止扩展,因为if条件不再为真,并且一切都开始自行崩溃,或者换句话说,它可以继续正常执行代码块。

答案 3 :(得分:1)

当你刚刚开始学习时,递归似乎会被混淆。首先,您可以想到一个子问题。对于preOder,它总是打印出根节点,左侧节点,然后是右侧节点。您所需要的只是解决一个子问题。 (下面一个简单的基本树)

     root
    /    \
  left  right
  /        \
...        ...

现在回过头来看看代码:

 if(root != null) {
    System.out.println(root);
    preOrder(root.leftChild);  // the left node now becomes another root, and keep making the next left node root until the next left node is null.
    preOrder(root.rightChild); // this line of code won't be executed until the last preOrder function call its root == null(hit the condition.)
 }

信任递归;如果您的情况正确,它将始终为您完成其余部分。我同意运行调试模式以便更好地理解。当您尝试理解代码如何工作时,学习如何使用“step in”非常有用。