【问题标题】:Counting number of nodes in Binary Tree [duplicate]计算二叉树中的节点数[重复]
【发布时间】:2016-12-08 17:30:30
【问题描述】:
class TreeNode {

TreeNode left;
TreeNode right;
char ch;

TreeNode(char ch){
    this.right = null;
    this.left = null;
    this.ch = ch;
}

TreeNode(TreeNode left, TreeNode right, char ch){
    this.left = left;
    this.right = right;
    this.ch = ch;
}
}

public class ExpressionTree {
public TreeNode root;

public void inorder() {
    inorder(root);
}

public void inorder(TreeNode node) {
    if (node != null){
        inorder(node.left);
        System.out.printf(node.ch + " ");
        inorder(node.right);
    }
}

public void preorder() {
    preorder(root);
}

public void preorder(TreeNode node) {
    if (node != null){
        System.out.printf(node.ch + " ");
        preorder(node.left);
        preorder(node.right);
    }
}

public void postorder() {
    postorder(root);
}
public void postorder(TreeNode node) {
    if (node != null){
        postorder(node.left);
        postorder(node.right);
        System.out.printf(node.ch + " ");
    }
}

public int size() {
    int countLeft = 0, countRight= 0;
    TreeNode nodeLeft = this.root.left;
    TreeNode nodeRight = this.root.right;
    if (this.root == null)
        return 0;

    if (this.root.left == null && this.root.right == null)
        return 1;

    while (nodeLeft != null){
        countLeft = countLeft + 1;
        nodeLeft = nodeLeft.left;
    }

    while (nodeRight != null){
        countRight = countRight + 1;
        nodeRight = nodeRight.right;
    }
    return 1 + countLeft + countRight;
}

public int recSize() { return recSize(root); }

public int recSize(TreeNode node) {
    int count = 0;
    if (node == null)
        return 0;
    if (node.left == null && node.right == null){
        return 1;
    }else{
        return 1 + recSize(node.left) + recSize(node.right);
    }
}
}

我想知道为什么迭代版本。无法找到此二叉树中的节点数?它似乎只能找到叶子的数量(如果我对这个说法也有错误,请纠正我)。

【问题讨论】:

  • @xenteros ,请删除重复的标签,它们是完全不同类型的问题。
  • 其实完全一样。
  • @xenteros ,您似乎并没有真正浏览整个页面。我不允许使用堆栈,而该页面使用 pop 和 push 功能来计数。我真的很好奇你的判断标准。
  • 那是XY problem

标签: java binary-tree


【解决方案1】:

我认为您在树节点的迭代计数方面遇到的问题是:

当您将 nodeLeft 重新分配给 nodeLeft.left 时,您只计算左节点的左子节点。如果您的根的左孩子有一个右孩子,则不会计算在内。右孩子则相反。实际上,迭代方法只计算构成二叉树最左边和最右边部分的节点

为了进一步澄清,在不直接使用递归的情况下计算树中节点的最佳方法可能是将左右子节点推入堆栈,将总数加一,将下一个节点从堆栈中弹出,如果有子节点,则推送该节点,并重复直到堆栈为空。

这样的事情应该可以解决问题:

Stack visitStack<TreeNode> = new Stack();
visitStack.push(this.root);
while(!visitStack.isEmpty()){
    count++;
    TreeNode next = visitStack.pop();
    if(next.left != null){
        visitStack.push(next.left);
    }
    if(next.right != null){
        visitStack.push(next.right);
    }
}

请注意,无法使用与使用递归一样优雅的迭代来遍历树的所有节点,因为树本质上是递归的。

此外,如果您不关心访问这些节点的顺序(如果您只是计算它们,看起来好像不会),那么您没有理由不能使用某些东西队列或动态数组的行或任何用于保存和删除节点以代替堆栈的行。

【讨论】:

  • 我在迭代方法的正下方实现了递归方法。我需要他们两个。有什么建议吗?
  • 是的,我看到了。由于您似乎正在学习数据结构,我只是想添加一些关于为什么递归解决方案更优雅的花絮。
  • 没错,递归更容易理解和实现。但是,处理大树可能会很麻烦。 (就节点数量而言)。所以.....迭代仍然是一种选择。
  • 当然,如果您需要进行迭代,并且只需要计算节点数,那么堆栈没有什么特别之处。您可以改用ArrayList&lt;TreeNode&gt;,然后添加/从中删除。
【解决方案2】:

错误 迭代版本有一个缺陷。它确实只计算rootleft-&gt;left-&gt;left-&gt;left-&gt;...right-&gt;right-&gt;right-&gt;right-&gt;...


因为,您没有反向指针来遍历内部节点。您必须使用堆栈来存储节点。


程序 [迭代版本]

您可以使用此代码计算树中的节点数。

public int size() {
    Stack<TreeNode> s = new Stack<>();
    s.push(root);
    int result=0;
    while(!s.isEmpty())
    {
        TreeNode top = s.peek();
        if(top.left!=null)
        {
            stack.push(top.left);
            top.left=null;
        }  
        else
        {
            result++;
            stack.pop();
            if(top.right!=null)
            {
                stack.push(top.right);
            }
        }
    }
    return result;
}

程序 [递归版本]

如果您不应该显式使用堆栈,您可以编写递归代码。但是,请注意,递归代码隐式使用堆栈。

private int countNodes(TreeNode n)
{
    if(n==null)
        return 0;
    else
    {
        return 1 + countNodes(n.left) + countNodes(n.right);
    }
}

public int size()
{
    return countNodes(root);
}

【讨论】:

  • 我不允许使用堆栈...。我见过很多使用堆栈完成的。还是谢谢。
  • 在树中,节点没有指向父节点的指针。您应该存储部分遍历的节点。即,只遍历了一棵子树。对于存储元素,您可以使用任何集合,但最终会使用类似于 push 和 pop 的方法(当然,如果它们存在于集合中)。
【解决方案3】:

您的迭代代码不起作用,因为它读取了一个节点,转到它的左侧(或右侧)并忘记了该节点的右侧(或左侧)子节点。您的代码假设每个节点都有一个右子节点或一个左子节点,而不是两者都有(根节点除外)。

 while (nodeLeft != null){
        countLeft = countLeft + 1;
        nodeLeft = nodeLeft.left; //Missed the right child here
    }
while (nodeRight != null){
        countRight = countRight + 1;
        nodeRight = nodeRight.right; //Missed the left child here
    } 

如果您不能使用堆栈,请使用队列。 广度优先遍历l遍历树的所有节点。广度优先遍历访问 node(N),保留 子节点(N1,N2 ....,在二叉树的情况下,Nl 和 Nr)可以从队列中的那个 node(N) 到达,并从队列中删除 node(N) 本身。类似地,从队列中获取一个节点,将它的子节点添加到队列中(如果它们之前没有被访问过),然后从队列中删除节点。

但如果您正在寻找一个恒定的空间遍历来计算节点,我建议您查看以下Geeks For Geeks, Constant Space traversal of binary tree

我不会给出广度优先遍历的代码。我想你可以自己做饭。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-05
    • 2020-02-21
    • 1970-01-01
    相关资源
    最近更新 更多