Given the root of a complete binary tree, your task is to count the total number of nodes in the tree. A complete binary tree is defined as a binary tree in which every level, except possibly the last, is completely filled, and all nodes are as far left as possible.
root
is a reference to the root node of the tree (or null
if the tree is empty).Constraints:
[0, 5 * 10^4]
.The most straightforward way to solve this problem is to traverse the entire tree and count each node. This brute-force approach would work for any binary tree. However, the problem specifically states that the tree is complete, which gives us an opportunity to optimize.
In a complete binary tree, all levels are fully filled except possibly the last, and all nodes in the last level are as far left as possible. This means that the structure is predictable, and we can use this property to avoid traversing every single node.
By comparing the height of the leftmost and rightmost paths, we can sometimes determine if a subtree is perfect (completely filled), allowing us to calculate the number of nodes in that subtree directly using the formula 2^h - 1
(where h
is the height).
This insight leads us to a much more efficient solution than simply counting nodes one by one.
Let's break down the optimized algorithm step by step:
2^h - 1
.1 + count(left) + count(right)
.null
, return 0.This approach leverages the properties of a complete binary tree to skip entire levels when possible, making it much faster than a naive traversal.
Consider the following complete binary tree:
1 / \ 2 3 / \ / 4 5 6
2^2 - 1 = 3
nodes (2, 4, 5).Brute-Force Approach:
h
is the height of the tree.The optimized approach is much faster for large trees because it can skip counting entire subtrees when they're perfect.
By leveraging the properties of complete binary trees, we avoid unnecessary traversal and make our solution highly efficient. The key insight is to recognize when a subtree is perfect and use the formula for the number of nodes directly, drastically reducing the number of recursive calls. This elegant strategy results in a solution that is both concise and performant, especially for large trees.
# Definition for a binary tree node.
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: TreeNode) -> int:
def left_height(node):
h = 0
while node:
h += 1
node = node.left
return h
def right_height(node):
h = 0
while node:
h += 1
node = node.right
return h
if not root:
return 0
lh = left_height(root)
rh = right_height(root)
if lh == rh:
return (1 << lh) - 1
else:
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
int leftHeight(TreeNode* node) {
int h = 0;
while (node) {
h++;
node = node->left;
}
return h;
}
int rightHeight(TreeNode* node) {
int h = 0;
while (node) {
h++;
node = node->right;
}
return h;
}
int countNodes(TreeNode* root) {
if (!root) return 0;
int lh = leftHeight(root);
int rh = rightHeight(root);
if (lh == rh) {
return (1 << lh) - 1;
} else {
return 1 + countNodes(root->left) + countNodes(root->right);
}
}
};
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
class Solution {
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;
} else {
return 1 + countNodes(root.left) + countNodes(root.right);
}
}
private int leftHeight(TreeNode node) {
int h = 0;
while (node != null) {
h++;
node = node.left;
}
return h;
}
private int rightHeight(TreeNode node) {
int h = 0;
while (node != null) {
h++;
node = node.right;
}
return h;
}
}
/**
* 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) {
function leftHeight(node) {
let h = 0;
while (node) {
h++;
node = node.left;
}
return h;
}
function rightHeight(node) {
let h = 0;
while (node) {
h++;
node = node.right;
}
return h;
}
if (!root) return 0;
let lh = leftHeight(root);
let rh = rightHeight(root);
if (lh === rh) {
return (1 << lh) - 1;
} else {
return 1 + countNodes(root.left) + countNodes(root.right);
}
};