Counting number of nodes in a complete binary tree

16,227

Solution 1

Suppose that we start off by walking down the left and right spines of the tree to determine their heights. We'll either find that they're the same, in which case the last row is full, or we'll find that they're different. If the heights come back the same (say the height is h), then we know that there are 2h - 1 nodes and we're done. (refer figure below for reference)

enter image description here

Otherwise, the heights must be h+1 and h, respectively. We know that there are then at least 2h - 1 nodes, plus the number of nodes in the bottom layer of the tree. The question, then, is how to figure that out. One way to do this would be to find the rightmost node in the last layer. If you know at which index that node is, you know exactly how many nodes are in the last layer, so you can add that to 2h - 1 and you're done.

enter image description here

If you have a complete binary tree with left height h+1, then there are between 1 and 2h - 1 possible nodes that could be in the last layer. The question is then how to determine this as efficiently as possible.

Fortunately, since we know the nodes in the last layer get filled in from the left to the right, we can use binary search to try to figure out where the last filled node in the last layer is. Essentially, we guess the index where it might be, walk from the root of the tree down to where that leaf should be, and then either find a node there (so we know that the rightmost node in the bottom layer is either that node or to the right) or we don't (so we know that the rightmost node in the bottom layer must purely be to the right of the current location). We can walk down to where the kth node in the bottom layer should be by using the bits in the number k to guide a search down: we start at the root, then go left if the first bit of k is 0 and right if the first bit of k is 1, then use the remaining bits in a corresponding manner to walk down the tree. The total number of times we'll do this is O(h) and each probe takes time O(h), so the total work done here is O(h2). Since h is the height of the tree, we know that h = O(log n), so this algorithm takes time O(log2 n) time to complete.

I'm not sure whether it's possible to improve upon this algorithm. I can get an Ω(log n) lower bound on any correct algorithm, though. I'm going to argue that any algorithm that is always correct in all cases must inspect the rightmost leaf node in the final row of the tree. To see why, suppose there's a tree T where the algorithm doesn't do this. Let's suppose that the rightmost node that the algorithm inspects in the bottom row is x, that the actual rightmost node in the bottom row is y, and that the leftmost missing node in the bottom row that the algorithm detected is z. We know that x must be to the left of y (because the algorithm didn't inspect the leftmost node in the bottom row) and that y must be to the left of z (because y exists and z doesn't, so z must be further to the right than y). If you think about what the algorithm's "knowledge" is at this point, the algorithm has no idea whether or not there are any nodes purely to the right of x or purely to the left of z. Therefore, if we were to give it a modified tree T' where we deleted y, the algorithm wouldn't notice that anything had changed and would have exactly the same execution path on T and T'. However, since T and T' have a different number of nodes, the algorithm has to be wrong on at least one of them. Inspecting this node takes time at least Ω(log n) because of the time required to walk down the tree.

In short, you can do better than O(n) with the above O(log2 n)-time algorithm, and you might be able to do even better than that, though I'm not entirely sure how or whether that's possible. I suspect it isn't because I suspect that binary search is the optimal way to check the bottom row and the lengths of the paths down to the nodes you'd probe, even after taking into account that they share nodes in common, is Θ(log2 n), but I'm not sure how to prove it.

Hope this helps!

Images source

Solution 2

public int leftHeight(TreeNode root){
    int h=0;
    while(root!=null){
        root=root.left;
        h++;
    }
    return h;
}
 public int rightHeight(TreeNode root){
    int h=0;
    while(root!=null){
        root=root.right;
        h++;
    }
    return h;
}
public int countNodes(TreeNode root) {
    if(root==null)
        return 0;
    int lh=leftHeight(root);
    int rh=rightHeight(root);
    if(lh==rh)
        return (1<<lh)-1;
    return countNodes(root.left)+countNodes(root.right)+1;

}

In each recursive call,we need to traverse along the left and right boundaries of the complete binary tree to compute the left and right height. If they are equal the tree is full with 2^h-1 nodes.Otherwise we recurse on the left subtree and right subtree. The first call is from the root (level=0) which take O(h) time to get left and right height.We have recurse till we get a subtree which is full binary tree.In worst case it can happen that the we go till the leaf node. So the complexity will be (h + (h-1) +(h-2) + ... + 0)= (h(h+1)/2)= O(h^2).Also space complexity is size of the call stack,which is O(h). NOTE:For complete binary tree h=log(n).

Solution 3

If the binary tree is definitely complete (as opposed to 'nearly complete' or 'almost complete' as defined in the Wikipedia article) you should simply descend down one branch of the tree down to the leaf. This will be O(logn). Then sum the powers of two up to this depth. So 2^0 + 2^1... + 2^d

Solution 4

C# Sample might helps others. This is similar to the time complexity well explained above by templatetypedef

public int GetLeftHeight(TreeNode treeNode)
{
    int heightCnt = 0;

    while (treeNode != null)
    {
        heightCnt++;
        treeNode = treeNode.LeftNode;
    }
    return heightCnt;
}

public int CountNodes(TreeNode treeNode)
{
    int heightIndx = GetLeftHeight(treeNode);

    int nodeCnt = 0;

    while (treeNode != null)
    {
        int rightHeight = GetLeftHeight(treeNode.RightNode);

        nodeCnt += (int)Math.Pow(2, rightHeight); //(1 << rh);

        treeNode = (rightHeight == heightIndx - 1) ? treeNode.RightNode : treeNode.LeftNode;

        heightIndx--;
    }

    return nodeCnt;
}
Share:
16,227
Atul Kumar Verma
Author by

Atul Kumar Verma

Updated on June 04, 2022

Comments

  • Atul Kumar Verma
    Atul Kumar Verma almost 2 years

    I want to count the number of nodes in a Complete Binary tree but all I can think of is traversing the entire tree. This will be a O(n) algorithm where n is the number of nodes in the tree. what could be the most efficient algorithm to achieve this?