Java 递归如何遍历树

Java 递归如何遍历树,java,recursion,binary-tree,binary-search-tree,Java,Recursion,Binary Tree,Binary Search Tree,我正在研究树以及如何使用递归遍历它们。但我对它的工作原理感到困惑 java version "1.8.0_92" public void预订单(二进制树根){ if(root!=null){ System.out.println(根); 前序(root.leftChild)它并不总是调用自己。它会一直运行,直到它的底部出现一个空的leftChild。然后执行返回而不做任何事情——它备份一个级别并在最低级别父节点的rightChild上递归。当该调用也用完子节点时,执行会从处理该最低级别父节点

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

java version "1.8.0_92"
public void预订单(二进制树根){
if(root!=null){
System.out.println(根);

前序(root.leftChild)它并不总是调用自己。它会一直运行,直到它的底部出现一个空的leftChild。然后执行返回而不做任何事情——它备份一个级别并在最低级别父节点的rightChild上递归。当该调用也用完子节点时,执行会从处理该最低级别父节点返回,同样是b备份一个调用,并执行该节点的rightChild…直到遍历整个树。

它并不总是调用自己。它一直运行,直到它以空leftChild见底。然后执行返回,而不做任何操作—它备份一个级别并在最低级别父节点的rightChild上递归。当l也会耗尽子节点,执行从处理此最低级别的父节点返回,再次备份一个调用,并执行该节点的rightChild…直到遍历整个树。

一次
root。leftChild
变为null(即,当到达树的叶子时),
preOrder
将按如下方式调用:
preOrder(null)
。发生这种情况时,条件将求值为
false
,递归将展开并停止,此时将求值
preOrder(root.rightChild)

下面是一个调用跟踪(在Scala中):


一旦
root.leftChild
变为null(即,当您到达树的一个叶子时),
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
public void预订单(二进制树根){
if(root!=null){
System.out.println(根);

preOrder(root.leftChild);把它想象成打开一大堆门,直到你找到你要找的东西,然后你必须返回并关闭/检查其他的门

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
public void预订单(二进制树根){
if(root!=null){
System.out.println(根);

预排序(root.leftChild);当您刚开始学习时,递归可能会感到困惑。首先,您可以想到一个子问题。对于预排序,它总是打印出根节点、左节点,然后是右节点。您所需要的就是解出一个子问题。(下面是一个基本的简单树)

现在返回查看代码:

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

信任递归;如果您的条件正确,它将始终为您完成其余部分。我同意运行调试模式以更好地理解。学习如何使用“步进”非常有用当你试图理解代码是如何工作的。

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

现在返回查看代码:

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

信任递归;如果您的条件正确,它将始终为您完成其余部分。我同意运行调试模式以更好地理解。学习如何使用“步进”非常有用当你试图理解代码是如何工作的。

第二个预排序总是会被调用,因为
root!=null
顺便说一句,我知道这个问题以前至少被问过两次;经过10分钟的反复检查后,我找不到前面的问题。@Paulo所以当第一个预排序(root.left)==null时,第二个预排序(root.right)将被调用?当第二个root.right==null时。将再次调用第一个前序(root.left)。直到两者都==为null。这是否正确?@ant2009 JVM会逐条指令执行,因为没有异常。在您的情况下,如果第一条指令执行
preOrder(root.leftChild)
并且它不会引发任何异常,我可以向您保证第二条指令
预排序(root.rightChild);
也将被执行。你应该使用调试模式,一步一步地执行你的代码,以了解发生了什么。第二个预序将始终被调用,因为
root!=null
顺便说一句,我知道这个问题以前至少被问过两次;我在10分钟的搜索后找不到前面的问题。@Paulo所以当第一个预序被调用时eOrder(root.left)==null。当第二个root.right==null时,将调用第二个前序(root.right)。第一个前序(root.left)将再次调用。直到两者都==为null。这是否正确?@ant2009 JVM逐指令执行指令,因为没有异常。在您的情况下,如果第一条指令执行
preOrder(root.leftChild)
,并且它没有引发任何异常,我可以向您确保第二条指令
preOrder(root.rightChild);
也将被执行。您应该使用调试模式,一步一步地执行代码,以了解发生了什么。
     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.)
 }