HomeLeetcode222. Count Complete Tree Nodes - Leetcode Solutions

222. Count Complete Tree Nodes – Leetcode Solutions

Description

Given the root of a complete binary tree, return the number of the nodes in the tree.

According to Wikipedia, every level, except possibly the last, is completely filled in a complete binary tree, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.

Design an algorithm that runs in less than O(n) time complexity.

Examples:

Example 1:

Input: root = [1,2,3,4,5,6]
Output: 6

Example 2:

Input: root = []
Output: 0

Example 3:

Input: root = [1]
Output: 1

Solution in Python

Approach

  1. Tree Height:
    • The height of a complete binary tree can be easily determined by following the leftmost path from the root. This gives the total number of levels.
  2. Binary Search on the Last Level:
    • The total number of nodes can be determined based on the height of the tree. However, the last level might not be completely full, so we use binary search to determine how many nodes are present in the last level.
    • Each level before the last is fully filled, so the number of nodes for all levels except the last can be directly calculated.
    • For the last level, we can perform a binary search to determine how many nodes are present.
  3. Time Complexity:
    • Since the tree is complete, the height is at most log(n). Binary search on the last level also takes log(n) steps, leading to an overall complexity of O(log(n)2).
Python
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        # Helper function to calculate the depth of the tree (i.e., height)
        def get_depth(node):
            depth = 0
            while node:
                node = node.left
                depth += 1
            return depth
        
        # If the tree is empty, return 0
        if not root:
            return 0
        
        # Calculate the depth of the left and right subtrees
        left_depth = get_depth(root.left)
        right_depth = get_depth(root.right)
        
        # If the left and right subtree depths are the same, the left subtree is a perfect binary tree
        # Therefore, we can add the nodes from the left subtree (which is 2^left_depth - 1) plus the root,
        # and then recurse on the right subtree.
        if left_depth == right_depth:
            return (1 << left_depth) + self.countNodes(root.right)
        else:
            # If the left and right depths are different, the right subtree is a perfect binary tree
            # with height one less than the left subtree. We can count all nodes in the right subtree
            # and recurse on the left subtree.
            return (1 << right_depth) + self.countNodes(root.left)

Detailed Explanation

  1. get_depth helper function:
    • This function calculates the depth (height) of the tree by following the leftmost path. It keeps moving left from the root node until it reaches None and counts how many steps it took. This gives the depth of the tree.
  2. Recursive counting logic:
    • We calculate the depth of the left subtree (left_depth) and the right subtree (right_depth).
    • If the depths are the same, it means the left subtree is a perfect binary tree (fully filled), so we can calculate the number of nodes in the left subtree as 2^left_depth - 1, then add 1 for the root node and recurse on the right subtree.
    • If the depths are different, the right subtree is a perfect binary tree, and we calculate its number of nodes as 2^right_depth - 1, then add 1 for the root node and recurse on the left subtree.
  3. Base Case:
    • If the tree is empty (root is None), we return 0 because there are no nodes.

Time Complexity

  • Time complexity: O(log(n)2). Calculating the depth takes O(log(n)), and we perform this calculation recursively while recursing into either the left or right subtree, resulting in an overall time complexity of O(log(n) * log(n)).
  • Space complexity: O(log(n)) due to the recursion stack depth in a balanced binary tree.

This approach efficiently counts the nodes in a complete binary tree in less than O(n) time.

Solution in Javascript

JavaScript
/**
 * @param {TreeNode} root
 * @return {number}
 */
var countNodes = function(root) {
    // Helper function to calculate the depth of the tree
    const getDepth = (node) => {
        let depth = 0;
        while (node) {
            node = node.left;
            depth++;
        }
        return depth;
    };

    // If the tree is empty, return 0
    if (!root) return 0;

    // Calculate the depth of the left and right subtrees
    let leftDepth = getDepth(root.left);
    let rightDepth = getDepth(root.right);

    // If the left and right depths are the same, it means the left subtree is a perfect binary tree
    // Therefore, the number of nodes in the left subtree is 2^leftDepth - 1, plus the root node,
    // and we need to recursively count nodes in the right subtree.
    if (leftDepth === rightDepth) {
        return (1 << leftDepth) + countNodes(root.right);
    } else {
        // If the depths are different, it means the right subtree is a perfect binary tree
        // with height rightDepth, so we can count all nodes in the right subtree,
        // plus the root node, and then recursively count nodes in the left subtree.
        return (1 << rightDepth) + countNodes(root.left);
    }
};

Solution in Java

Java
class Solution {
    // Helper function to calculate the depth of the tree by going down the leftmost path
    private int getDepth(TreeNode node) {
        int depth = 0;
        while (node != null) {
            node = node.left;
            depth++;
        }
        return depth;
    }
    
    public int countNodes(TreeNode root) {
        // If the tree is empty, return 0
        if (root == null) return 0;
        
        // Calculate the depth of the left and right subtrees
        int leftDepth = getDepth(root.left);
        int rightDepth = getDepth(root.right);
        
        // If the depths are the same, it means the left subtree is a perfect binary tree
        if (leftDepth == rightDepth) {
            // The number of nodes in the left subtree is 2^leftDepth - 1 (plus the root node)
            // We then recurse on the right subtree to count its nodes
            return (1 << leftDepth) + countNodes(root.right);
        } else {
            // If the depths are different, it means the right subtree is a perfect binary tree
            // The number of nodes in the right subtree is 2^rightDepth - 1 (plus the root node)
            // We then recurse on the left subtree to count its nodes
            return (1 << rightDepth) + countNodes(root.left);
        }
    }
}

Solution in C#

C#
public class Solution {
    // Helper function to calculate the depth by going down the leftmost path
    private int GetDepth(TreeNode node) {
        int depth = 0;
        while (node != null) {
            node = node.left;
            depth++;
        }
        return depth;
    }

    public int CountNodes(TreeNode root) {
        // Base case: if the tree is empty, return 0
        if (root == null) return 0;
        
        // Calculate the depth of the left and right subtrees
        int leftDepth = GetDepth(root.left);
        int rightDepth = GetDepth(root.right);
        
        // If the left and right depths are the same, the left subtree is a perfect binary tree
        if (leftDepth == rightDepth) {
            // The number of nodes in the left subtree is 2^leftDepth - 1, plus the root node
            // We then recurse on the right subtree
            return (1 << leftDepth) + CountNodes(root.right);
        } else {
            // If depths are different, the right subtree is a perfect binary tree
            // The number of nodes in the right subtree is 2^rightDepth - 1, plus the root node
            // We then recurse on the left subtree
            return (1 << rightDepth) + CountNodes(root.left);
        }
    }
}

Subscribe
Notify of

0 Comments
Inline Feedbacks
View all comments

Popular