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.
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:
[0, 5 * 104].0 <= Node.val <= 5 * 104When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The goal is to find how many nodes are in the tree. The brute force way to do this is to simply visit every single node and count them.
Here's how the algorithm would work step-by-step:
def count_complete_tree_nodes_brute_force(root):
number_of_nodes = 0
def traverse_tree(node):
nonlocal number_of_nodes
if not node:
return
# We are visiting the node, so increment the count.
number_of_nodes += 1
traverse_tree(node.left)
traverse_tree(node.right)
# Start the traversal from the root
traverse_tree(root)
return number_of_nodesThe key to efficiently counting nodes in a complete tree is realizing it has a special structure. We don't need to visit every single node; instead, we use the tree's properties to quickly calculate the number of nodes.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def countNodes(self, root):
if not root:
return 0
def get_height(node):
height = 0
while node:
height += 1
node = node.left
return height
left_height = get_height(root.left)
right_height = get_height(root.right)
# If heights are equal, left subtree is perfect.
if left_height == right_height:
return (1 << left_height) + self.countNodes(root.right) + 0
# Otherwise, right subtree is complete but one level shorter.
else:
return (1 << right_height) + self.countNodes(root.left) + 0| Case | How to Handle |
|---|---|
| Null or empty tree | Return 0 immediately if the root is null, representing an empty tree. |
| Tree with only a root node | Return 1 if only a root node exists, as it represents one node in the tree. |
| Perfectly balanced complete binary tree | Utilize the formula 2^height - 1 to calculate efficiently without traversing all nodes. |
| Completely skewed tree (left or right) | Fall back to the O(n) traversal method, as the height calculation shortcut won't apply. |
| Maximum height tree (based on memory constraints) | Ensure that the recursion depth or iterative stack doesn't exceed memory limits, potentially using an iterative solution. |
| Integer overflow in height calculation or node count | Use appropriate data types (long) to avoid integer overflow during height calculation or when counting a large number of nodes. |
| Tree with a very deep left subtree and a shallow right subtree. | Height calculation may still be unbalanced and slow if the tree's right side is significantly shorter than the left. |
| Tree where the last level is not filled completely from left to right. | The solution should accurately count the number of nodes in the last level by checking each node. |