将节点添加到树 - 为啥根没有被初始化?

Posted

技术标签:

【中文标题】将节点添加到树 - 为啥根没有被初始化?【英文标题】:Adding node to tree - Why is the root not getting initialized?将节点添加到树 - 为什么根没有被初始化? 【发布时间】:2020-09-30 09:25:14 【问题描述】:

我编写了两个用于向树中添加新节点的函数:一个public 和一个private。私有的是递归的。公共的称为递归的。第一个问题:这是可以接受的做法吗?

public void addNode(int val) 
    addNode(val, root, null, 0);
    System.out.println("Root is null: " + (root == null));


private void addNode(int val, Node node, Node parent,int height)  
    
    if(node == null) 
        node = new Node(val, height, 0);
        System.out.println("is new node equal to root?"+(node == root));
        System.out.println("Added node on height: " + node.getHeight());
        return;
    
        height++;
        addNode(val, node.left, node, height);
        addNode(val, node.right, node, height);


现在问题来了:根变量没有被初始化。它在树类中声明。 public Node root;

这让我很困惑,因为我知道 Java 是按引用传递,而不是按值传递。为什么调用这些函数后root为空?

控制台输出:

is new node equal to root?false
Added node on height: 0
Root is null: true

【问题讨论】:

new Node(val, height, 0); 中的第三个参数是什么? 即大小(子树中节点的数量)@trincot 【参考方案1】:

如果在 Java 代码中函数为函数参数分配一个新值,这绝不会影响调用者可能已作为参数传递的变量。您可能对当参数变量变异时会发生什么感到困惑:例如,如果它是一个对象并且您为它的一个属性分配了不同的值,那么调用者可以看到此更改对象,因为它确实是同一个对象。但是对参数的简单赋值总是只会对该局部变量产生影响。

要使其工作,请将您的函数设计为返回您提供给它的节点(无论它是否有新值)。

还有一个问题:您当前在左右子树中都添加了一个新节点(如果它们存在的话),并且这会递归地重复。我假设您尝试在二叉树中插入值搜索,因此您应该选择在哪个子树中添加节点。

最后,不需要传递parentNodeheight 作为参数,因为您似乎将高度存储在每个节点中,因此您知道新节点的高度必须比存储的高度大一它的父节点(或者,如果不存在,则为 0)。

public void addNode(int val) 
    root = addNode(val, root);


private void addNode(int val, Node node)  
    if (node == null) 
        return new Node(val, 0, 0);  // NB: height will be updated when backtracking
    
    if (val < node.val) 
       node.left = addNode(val, node.left);
       node.left.height = node.height + 1;
     else 
       node.right = addNode(val, node.right);
       node.right.height = node.height + 1;
    
    return node;

最后,“高度”这个名字在这里有点误导,因为这个术语应该表示节点是根的(子)树的高度。但是这段代码中的height 代表了树中节点的深度。见What is the difference between tree depth and height?。

【讨论】:

一个非常有帮助和清晰的答案。谢谢! @trincot【参考方案2】:

当然,从公共方法调用私有方法(甚至是递归的)并没有错。 Root 为 null 仅仅是因为您正在为 node 参数分配新值,您并没有更改对象,而是创建了新对象。

关注

private void addNode(int val, Node node, Node parent,int height) 
   ...
   node = new Node(val, height, 0);

不会更改调用者中的参数node。所以调用后

addNode(val, root, null, 0);

root 保持不变(null 值)

还要记住对象在 Java 中是按值传递的

实际上(在 Java 中)在函数中,您只收到 node 的内存地址(值)(例如 x64 架构中的 000000D5098FFA70)。因此,如果您修改例如node.left 您实际上正在更改地址 000000D5098FFA70 + 4 的内存。但是,如果你改变 该地址 - 值 - 您无法访问该对象。从那一刻起,您只使用局部变量。这就是为什么它被称为按值传递。

【讨论】:

以上是关于将节点添加到树 - 为啥根没有被初始化?的主要内容,如果未能解决你的问题,请参考以下文章

VB.NET 如何将子节点添加到树视图中的特定节点

将节点添加到树问题[关闭]

将第一个节点添加到hashmap中的链表时,为啥必须将新节点直接分配给索引指针?

遍历查找

结构不是为节点创建的,但是在初始化节点时,如果我们在它之前编写结构,代码工作正常,没有错误。为啥?

将复选框列添加到树左侧的 QTreeView 的方法?