【问题标题】:Recursion while adding nodes in tree data structure在树数据结构中添加节点时递归
【发布时间】:2019-08-17 19:40:49
【问题描述】:

我是理解和学习数据结构的新手。在搜索了一些教程后,我尝试实现 Tree 数据结构。看起来不错,但我不明白这里有点奇怪的递归行为。可以有人帮助我理解代码。

我包含了一些打印语句来理解递归流程,但无法理解为什么没有返回当前引用?

public class TreeCheck {
Node root;
private Node addRecursive(Node current, int value) {
if (current == null) {
    return new Node(value);
}

if (value < current.value) {
    current.left = addRecursive(current.left, value);
} else if (value > current.value) {
    current.right = addRecursive(current.right, value);
} else {
    // value already exists
    return current;
}
System.out.println("current is "+current.value); //Please compare in the 
 image why the 
return current;
}


public void add(int value) {
root = addRecursive(root, value);
System.out.println("value is "+root.value);
}

  public static void main(String h[]){
 TreeCheck bt = new TreeCheck();

    bt.add(6);
    bt.add(4);
    bt.add(8);
    bt.add(3);
    bt.add(5);
    bt.add(7);
    bt.add(9);


    }

  class Node {
  int value;
  Node left;
  Node right;

   Node(int value) {
    this.value = value;
    right = null;
    left = null;
}
}

为什么当前语句被打印两次,并且总是在有时重新分配当前语句时才返回根元素?

【问题讨论】:

  • 您首先进行递归调用。这个递归调用可能会进行另一个递归调用。您的日志仅在所有递归完成后打印。为了更好地理解它,请尝试添加两个日志。首先在你的 addRecursive 方法的开头,比如println("START: current is" + ...),当前日志是println("END: current is" + ...)。甚至可能在您的 if 语句中添加更多日志,以清楚地查看它是添加到树的左侧还是右侧。更好的方法是拿起笔和纸一步一步地画树,看看它是如何创建的。
  • @ich5003 谢谢,在上面的例子中,查询是在添加 4 时,当前节点(最初是 6)但由于递归它变成了 4 并且它没有返回,而不是那个 6再次返回。为什么会出现这种行为?为什么递归函数返回语句被忽略?
  • "current" 并不总是您要添加的值。清楚地看看递归调用会发生什么。您将函数参数 current 设置为 current.left 或 current.right。因此,您正在逐个节点地遍历树,直到找到一个不再存在节点的位置,然后您将在那里添加您的值。确保您了解作为函数参数的“当前”和“值”之间的区别。

标签: java recursion tree


【解决方案1】:

打印不正确,原因如下:

首先,您的 add 方法总是打印“value is” + root.value”,这在试图弄清楚程序如何添加值时会令人困惑。

其次,您的 add 方法在插入值后打印,我将对其进行重组,使其首先打印要插入的值,然后打印正在检查节点的路径:

    public void add(int value) {
    // you always printed out the value of the root node with every call to this method
    // I updated it to reflect the input argument
    System.out.println("\nvalue is " + value);
    root = addRecursive(root, value);
}

现在每个块都是自己的插入,程序流程更容易追踪。

下一步:current 实际上是正确打印的,只是以相反的顺序。假设你要插入 5,当前要打印的节点将是:5 的父节点,即 4,然后是 4 的父节点,即 6。

这张图片可能有助于可视化这棵树(对不起,我的字写得很丑)

如果你想改变顺序,这样做(把 current 的 print 放在 if 语句之前):

    private Node addRecursive(Node current, int value) {
    if (current == null) {
        return new Node(value);
    }

    System.out.println("current is " + current.value);

    if (value < current.value) {
        current.left = addRecursive(current.left, value);
    } else if (value > current.value) {
        current.right = addRecursive(current.right, value);
    } else {
        // value already exists
        return current;
    }

    return current;
}

此外,如果您想查看插入二叉搜索树是否有效,可以使用此方法按升序打印树:

    public void inOrderPrint(Node node){

    if (node.left != null) {
        inOrderPrint(node.left);
    }

    System.out.println(node.value);

    if (node.right != null) {
        inOrderPrint(node.right);
    }
}

在你的主目录中这样称呼它:

    public static void main(String h[]) {
    TreeCheck bt = new TreeCheck();

    bt.add(6);
    bt.add(4);
    bt.add(8);
    bt.add(3);
    bt.add(5);
    bt.add(7);
    bt.add(9);

    System.out.println("-------");
    bt.inOrderPrint(bt.root);

}

我希望这会有所帮助,并且我已经清楚地解释了它。如果我的陈述不正确,请发表评论,我会相应地编辑帖子。

【讨论】:

  • 感谢您的回答,我唯一关心的是在遍历树时,根节点值 6 检查它的左节点位置(已被 4 占用并通过递归知道)现在之后递归为什么返回语句没有返回 4 节点的引用,而是将节点 6 再次返回到根。我的意思是无论方法中发生的递归次数如何,都会始终返回第一个调用引用?
  • 一般来说,想象如下:您有一个调用堆栈,其中存储了方法调用及其各自的本地变量。如果你连续 3 个递归调用 `A1(A2(A3)))`(A 是方法,数字只是我可以引用它们),它将首先将 A1 添加到调用堆栈,然后是 A2,然后A3。还没有任何方法返回。它们将以相反的顺序返回,因此(LIFO,后进先出)首先返回 A3,然后是 A2,然后是 A1。在这种情况下,每种方法都有自己的current 版本。无论哪个方法调用首先命中打印语句,都会先打印。
  • 尝试一步步调试程序。想象一下每个方法调用做了什么,传递了哪些参数并以这种方式跟踪程序。绘制树以及如何插入节点。使用它们各自的局部变量绘制方法调用,并查看何时到达 print 语句以及 current 的值。此外,程序是按顺序执行的,如果方法 A1 调用 A2,则 A1 将继续,直到 A2 返回。每一行都是按顺序处理的。如果 A1 在第 50 行调用 A2,A1 在那里等待,A2 将从第 1 行开始。一旦 A2 完成,A1 将在第 51 行继续。
【解决方案2】:

https://www.geeksforgeeks.org/binary-search-tree-set-1-search-and-insertion/

阅读上面的文章应该会有所帮助。 学习愉快!!

【讨论】:

  • -感谢您的回答,但查询是为什么随着电流不断变化,每次都没有返回递归返回语句。
  • 在遵循算法的同时手动绘制树可能会回答您的问题。
猜你喜欢
  • 2021-08-03
  • 2019-02-26
  • 2016-03-18
  • 2019-04-14
  • 2018-03-19
  • 2011-05-15
  • 2010-10-05
  • 2018-11-04
  • 1970-01-01
相关资源
最近更新 更多