222. Count Complete Tree Nodes
Problem Description
Given the root of a complete binary tree, return the number of 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.
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
Constraints:
- The number of nodes in the tree is in the range
[0, 5 * 10^4] 0 <= Node.val <= 5 * 10^4- The tree is guaranteed to be complete
Solution
Python Solution
class Solution:
def countNodes(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
# Get the height of left and right subtree
left_height = self.getLeftHeight(root)
right_height = self.getRightHeight(root)
# If heights are equal, use formula 2^h - 1
if left_height == right_height:
return (1 << left_height) - 1
# Otherwise, recursively count nodes
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
def getLeftHeight(self, node):
height = 0
while node:
height += 1
node = node.left
return height
def getRightHeight(self, node):
height = 0
while node:
height += 1
node = node.right
return height
Time Complexity: O(log²n)
We traverse down the tree O(log n) times, and at each step we calculate heights which takes O(log n).
Space Complexity: O(log n)
Due to the recursion stack.
Java Solution
class Solution {
public int countNodes(TreeNode root) {
if (root == null) {
return 0;
}
// Get the height of left and right subtree
int leftHeight = getLeftHeight(root);
int rightHeight = getRightHeight(root);
// If heights are equal, use formula 2^h - 1
if (leftHeight == rightHeight) {
return (1 << leftHeight) - 1;
}
// Otherwise, recursively count nodes
return 1 + countNodes(root.left) + countNodes(root.right);
}
private int getLeftHeight(TreeNode node) {
int height = 0;
while (node != null) {
height++;
node = node.left;
}
return height;
}
private int getRightHeight(TreeNode node) {
int height = 0;
while (node != null) {
height++;
node = node.right;
}
return height;
}
}
Time Complexity: O(log²n)
We traverse down the tree O(log n) times, and at each step we calculate heights which takes O(log n).
Space Complexity: O(log n)
Due to the recursion stack.
C++ Solution
class Solution {
public:
int countNodes(TreeNode* root) {
if (!root) {
return 0;
}
// Get the height of left and right subtree
int leftHeight = getLeftHeight(root);
int rightHeight = getRightHeight(root);
// If heights are equal, use formula 2^h - 1
if (leftHeight == rightHeight) {
return (1 << leftHeight) - 1;
}
// Otherwise, recursively count nodes
return 1 + countNodes(root->left) + countNodes(root->right);
}
private:
int getLeftHeight(TreeNode* node) {
int height = 0;
while (node) {
height++;
node = node->left;
}
return height;
}
int getRightHeight(TreeNode* node) {
int height = 0;
while (node) {
height++;
node = node->right;
}
return height;
}
};
Time Complexity: O(log²n)
We traverse down the tree O(log n) times, and at each step we calculate heights which takes O(log n).
Space Complexity: O(log n)
Due to the recursion stack.
JavaScript Solution
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var countNodes = function(root) {
if (!root) {
return 0;
}
// Get the height of left and right subtree
const leftHeight = getLeftHeight(root);
const rightHeight = getRightHeight(root);
// If heights are equal, use formula 2^h - 1
if (leftHeight === rightHeight) {
return (1 << leftHeight) - 1;
}
// Otherwise, recursively count nodes
return 1 + countNodes(root.left) + countNodes(root.right);
};
function getLeftHeight(node) {
let height = 0;
while (node) {
height++;
node = node.left;
}
return height;
}
function getRightHeight(node) {
let height = 0;
while (node) {
height++;
node = node.right;
}
return height;
}
Time Complexity: O(log²n)
We traverse down the tree O(log n) times, and at each step we calculate heights which takes O(log n).
Space Complexity: O(log n)
Due to the recursion stack.
C# Solution
public class Solution {
public int CountNodes(TreeNode root) {
if (root == null) {
return 0;
}
// Get the height of left and right subtree
int leftHeight = GetLeftHeight(root);
int rightHeight = GetRightHeight(root);
// If heights are equal, use formula 2^h - 1
if (leftHeight == rightHeight) {
return (1 << leftHeight) - 1;
}
// Otherwise, recursively count nodes
return 1 + CountNodes(root.left) + CountNodes(root.right);
}
private int GetLeftHeight(TreeNode node) {
int height = 0;
while (node != null) {
height++;
node = node.left;
}
return height;
}
private int GetRightHeight(TreeNode node) {
int height = 0;
while (node != null) {
height++;
node = node.right;
}
return height;
}
}
Time Complexity: O(log²n)
We traverse down the tree O(log n) times, and at each step we calculate heights which takes O(log n).
Space Complexity: O(log n)
Due to the recursion stack.
Approach Explanation
The solution takes advantage of the properties of a complete binary tree to achieve better than O(n) time complexity. Here's how it works:
- For a complete binary tree:
- If the left and right subtree heights are equal, the tree is a perfect binary tree
- For a perfect binary tree of height h, we can calculate the number of nodes using the formula 2^h - 1
- If the heights are not equal, we need to recursively count nodes in both subtrees
- The algorithm:
- Calculate the height of the leftmost path and rightmost path
- If they're equal, use the formula 2^h - 1
- Otherwise, recursively count nodes in left and right subtrees
The key insight is that we don't need to visit every node. By checking if subtrees are perfect binary trees, we can calculate their node count in O(1) time using the formula. This leads to a time complexity of O(log²n) instead of O(n).