计算二叉树中的节点数

时间:2016-11-14 05:48:31

标签: java binary-tree

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);
    }
}
}

我想知道为什么迭代ver。是不是要找到这个二叉树中的节点数?它似乎只能找到叶子的数量(如果我对这个陈述也不对,请纠正我。)

3 个答案:

答案 0 :(得分: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);
    }
}

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

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

答案 1 :(得分:1)

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

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

计划 [迭代版]

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

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);
}

答案 2 :(得分:0)

您的迭代代码无法正常工作,因为它会读取一个节点,向左(或向右)移动并忘记该节点的右(或左)子节点。您的代码假定每个节点都有一个右子节点或一个子节点,而不是两个节点(根节点除外)。

 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遍历树的所有节点。广度优先遍历访问节点(N),保留子节点(N1,N2 ....,如果是二叉树,Nl和Nr)可以从队列中的节点(N)到达,并从队列中删除节点(N)。同样地,从队列中获取节点,将其添加到队列中(如果之前没有访问过)并从队列中删除节点。

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

我没有给出广度优先遍历的代码。我假设你可以自己烹饪。