Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
A naive traversal counts in O(n), but a complete tree's structure lets you do better. At each node compare the left-spine height (keep going left) with the right-spine height (keep going right). If they're equal the subtree is perfect, so it has exactly 2^height − 1 nodes — return that in O(1) without recursing. Otherwise recurse into both children. The two heights are O(log n) each and you only recurse down one genuinely-incomplete spine, giving O(log² n) total.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you go beyond the trivial O(n) count and exploit the completeness guarantee — recognizing that a perfect subtree has a closed-form size and that comparing left and right spine heights detects perfection in O(log n).
Where candidates slip
Just returning 1 + count(left) + count(right) (correct but O(n)), or mishandling the perfect-tree formula (off-by-one on 2^h vs 2^h − 1, or measuring height as edges vs nodes inconsistently).
How to stand out
Derive the O(log² n) bound out loud — O(log n) height checks at O(log n) depth — and explain WHY only one spine per level is incomplete, which is the crux of why the recursion stays logarithmic.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewAt node: L = left-spine height, R = right-spine heightL == R ?YesNoExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Naive Full Traversal
1Hint 1Explore hintHide hint
Forget the completeness guarantee for a moment — how would you count nodes in ANY binary tree?
2Hint 2Explore hintHide hint
Every node contributes 1. The count of a subtree is 1 plus the counts of its two children.
3Hint 3Explore hintHide hint
That's a one-line recursion, but it touches every node, so it's O(n) — the problem asks for better.
A · Core idea
The most direct count ignores the completeness hint entirely: a tree's node count is 1 for the root plus the counts of the left and right subtrees, bottoming out at 0 for a null. This is correct for any binary tree.
Why it works. Counting is a textbook fold over the tree — each node is responsible for adding itself plus delegating to its children. There is no subtlety and no edge case beyond the null base returning 0.
Why it isn't the intended answer. It visits every one of the n nodes, so it is O(n). The problem statement specifically asks for less than O(n), which is a strong hint that the completeness property is meant to be exploited. Presenting this first, then saying 'but we can use the structure to do better', is exactly the progression an interviewer wants.
Why we still mention it. It anchors correctness and gives a reference answer to sanity-check the optimized version against. On a small or arbitrary tree it's also genuinely the right call — there's no point being clever when n is tiny or the tree isn't complete.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
root = [1,2,3,4,5,6]count(4),count(5)
each leaf → 1
leaves return 16C · Code with teaching comments
Reference implementation preview// Definition for a binary tree node. class TreeNode { int val; TreeNode left; TreeNode right;Explore the full codeHide the full code
D · Why it works and complexityO(n) timeO(h) spaceOpen the invariant and proofExplore the proofHide the proof
Counting is a pure structural fold: 1 + left + right. The optimized solution doesn't change what we compute, only how much of the tree we have to touch to compute it — that reframe is the whole game.
ComplexityO(n) time, O(h) space — Visits all n nodes once, O(1) work each → O(n) time. Recursion depth is the height h → O(h) stack space.
E · Edge cases and pitfalls6root = [] — Null root → 0.Explore edge casesHide edge cases
Edge cases handled
root = []Null root → 0.root = [1]1 + 0 + 0 = 1.Large complete treeCorrect answer but O(n) work — fails the 'better than O(n)' requirement.
Pitfalls
- Submitting this as the final answer when the prompt demands sub-linear time.
- Forgetting the null base case and dereferencing a missing child.
- Counting edges instead of nodes (off-by-one).
Approach 2 of 2 · Optimal
Spine-Height Perfect-Subtree Shortcut (Optimal, O(log² n))
1Hint 1Explore hintHide hint
In a complete tree, walk only left from a node to get one depth, and only right to get another. What does it mean if those two depths are equal?
2Hint 2Explore hintHide hint
Equal left and right spine heights means every level is full — the subtree is PERFECT, and a perfect tree of height h has exactly 2^h − 1 nodes. No need to count them one by one.
3Hint 3Explore hintHide hint
If the spines differ, the subtree is not perfect, so recurse into both children. Crucially, only one child's subtree is incomplete, so the recursion descends a single path — about log n levels, each doing an O(log n) height check.
A · Core idea
The completeness guarantee is not decoration — it is the lever that turns linear counting into logarithmic. The key observation: from any node, if you walk only left children you trace the left spine, and if you walk only right children you trace the right spine. In a complete tree, comparing the lengths of these two spines tells you whether the subtree is perfect (every level completely full).
Why equal spines imply a perfect subtree. Because the tree is complete, the last level fills strictly left-to-right. The left spine reaches the deepest, leftmost node; the right spine reaches the rightmost node that exists. If these two depths are equal, the bottom level must be entirely filled from left to right — there is no room for a gap, since a gap would shorten the right spine before the left. A subtree whose every level is full is perfect, and a perfect tree of spine height h (counting nodes) has exactly 2^h − 1 nodes. So when L == R we return (1 << L) - 1 in O(1) — we never touch the interior nodes at all.
Why unequal spines mean exactly one split. If L != R (specifically L > R), the subtree is not perfect, so we fall back to 1 + count(left) + count(right). This looks like it could blow up to O(n), but completeness saves us: at any node, at most one of its two children's subtrees is itself imperfect. The other is guaranteed perfect and will be answered by the formula in O(log n) on the very next call. So although we issue two recursive calls, only one of them ever recurses further — the recursion effectively walks a single root-to-bottom path.
Why the total is O(log² n). The recursion descends O(log n) levels (one path down a tree of height log n). At each level we compute two spine heights, and each spine walk is O(log n) because the tree's height is logarithmic in n. Multiplying the two logarithmic factors gives O(log² n) — dramatically better than O(n) for large trees. The space is just the O(log n) recursion depth.
Why the bit shift and the off-by-one matter. 2^h is computed as 1 << h, and we subtract 1 because a perfect tree of height h has 1 + 2 + 4 + … + 2^(h-1) = 2^h − 1 nodes. Counting the spine in nodes (empty tree height 0, single node height 1) keeps this formula clean. Mixing up edge-height and node-height is the classic source of an off-by-one here.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
root = [1,2,3,4,5,6]Interactive diagram
Spines on [1,2,3,4,5,6]
node 1Left spine 1→2→4 has height 3; right spine 1→3 has height 2 (3 has no right child) — unequal, so the root splits.
6C · Code with teaching comments
D · Why it works and complexityO(log² n) timeO(log n) spaceOpen the invariant and proofExplore the proofHide the proof
The completeness guarantee means imperfection lives on a single spine: at every node, one child is a perfect tree (closed-form) and only the other can be ragged. That's why two recursive calls still yield logarithmic depth — you're really binary-searching for the boundary between the full part and the empty part of the last level.
ComplexityO(log² n) time, O(log n) space — At each node we measure two spine heights, each O(log n). We recurse down at most one incomplete spine, so there are O(log n) levels of recursion. O(log n) levels × O(log n) per level = O(log² n) time. Recursion depth is O(log n) → O(log n) stack space.
E · Edge cases and pitfalls7root = [1] — leftHeight = rightHeight = 1 → perfect → (1<<1)-1 = 1.Explore edge casesHide edge cases
Edge cases handled
root = [1]leftHeight = rightHeight = 1 → perfect → (1<<1)-1 = 1.Perfect tree of 7 nodesAt the root left == right == 3 → return (1<<3)-1 = 7 with zero recursion.root = []Null root → 0 before any height check.
Pitfalls
- Computing 2^h instead of 2^h − 1 (off-by-one) — a perfect tree of height h has 2^h − 1 nodes.
- Mixing edge-height and node-height conventions between the two spine helpers.
- Re-walking full heights at every recursive call without realizing only one branch recurses — still O(log² n), but don't panic-optimize into incorrectness.
- Using 1 << left when left could exceed 31 (not an issue here given n ≤ 5×10⁴, but use long for very tall trees).
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic int countNodes(TreeNode root) { if (root == null) return 0; int left = leftHeight(root); int right = rightHeight(root); if (left == right) {Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Off-by-one in the perfect-tree formula
A perfect tree of node-height h has 2^h − 1 nodes, not 2^h. Returning 1 << left without subtracting 1 overcounts by exactly the (nonexistent) extra bottom row.
Explore the code comparisonHide the code comparison
Off-by-one in the perfect-tree formula
A perfect tree of node-height h has 2^h − 1 nodes, not 2^h. Returning 1 << left without subtracting 1 overcounts by exactly the (nonexistent) extra bottom row.
Don't write this
if (left == right) {
return (1 << left); // wrong: counts one phantom level
}Write this instead
if (left == right) {
return (1 << left) - 1; // perfect tree of height h has 2^h − 1 nodes
}2Inconsistent height measurement
If leftHeight counts nodes but rightHeight counts edges, the comparison left == right is meaningless and you'll misclassify perfect subtrees. Keep both identical.
Explore the code comparisonHide the code comparison
Inconsistent height measurement
If leftHeight counts nodes but rightHeight counts edges, the comparison left == right is meaningless and you'll misclassify perfect subtrees. Keep both identical.
Don't write this
def leftHeight(self, node):
h = 0
while node.left: # counts edges, stops one short, and crashes on leaf?
h += 1
node = node.left
return h
def rightHeight(self, node):
h = 0
while node: # counts nodes — INCONSISTENT with leftHeight
h += 1
node = node.right
return hWrite this instead
def leftHeight(self, node):
h = 0
while node is not None: # both count NODES
h += 1
node = node.left
return h
def rightHeight(self, node):
h = 0
while node is not None:
h += 1
node = node.right
return h3Falling back to plain O(n) recursion everywhere
Recursing 1 + count(left) + count(right) unconditionally never uses the formula, so it visits every node. The shortcut must fire whenever left == right.
Explore the code comparisonHide the code comparison
Falling back to plain O(n) recursion everywhere
Recursing 1 + count(left) + count(right) unconditionally never uses the formula, so it visits every node. The shortcut must fire whenever left == right.
Don't write this
public int countNodes(TreeNode root) {
if (root == null) return 0;
return 1 + countNodes(root.left) + countNodes(root.right); // always O(n)
}Write this instead
public int countNodes(TreeNode root) {
if (root == null) return 0;
int left = leftHeight(root), right = rightHeight(root);
if (left == right) return (1 << left) - 1; // O(1) for perfect subtrees
return 1 + countNodes(root.left) + countNodes(root.right);
}Revise
Carry the pattern into the interview
Pattern to remember
Perfect-subtree shortcut via left-spine vs right-spine height
Code pattern to memorize
count(node):
if node is null: return 0
L = leftSpineHeight(node) # keep going .left
R = rightSpineHeight(node) # keep going .right
if L == R: return (1 << L) - 1 # perfect → closed form
return 1 + count(node.left) + count(node.right)Rules to remember
- 1If left-spine height == right-spine height, the subtree is perfect →
2^h − 1nodes, no recursion needed. - 2Measure heights by walking only left and only right — that's
O(h)each, not a full traversal. - 3Use
1 << h(bit shift) for2^h, then subtract 1 — but ensurehfits so the shift doesn't overflow. - 4Only one child subtree per level is genuinely incomplete, so the recursion follows a single path →
O(log n)deep. - 5A complete tree is filled left-to-right on the last level — that property is exactly what makes the spine test valid.
Use this pattern when
- Counting nodes in a guaranteed COMPLETE (or perfect) binary tree faster than O(n)
- You can exploit the filled-left-to-right shape rather than visiting every node
- The tree is tall and you want sub-linear work
- Any problem where a perfect subtree's size has a closed form
Choose another pattern when
- Tree is arbitrary / not complete → you must visit every node, O(n)
- You need to process each node's value anyway → counting cheaply gains nothing
- Tree is tiny → the O(n) traversal is simpler and fast enough
- You need the count of leaves or a specific level, not the total
Interview takeaway
Count Complete Tree Nodes is the textbook case of exploiting structure to beat a linear scan: the completeness guarantee means most subtrees are perfect and answerable by formula. Spotting that left-spine == right-spine ⇒ perfect ⇒ 2^h − 1 turns O(n) into O(log² n).
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternExploit structural guarantees to beat a linear scan. Recognise it when a tree is promised complete/perfect: perfect subtrees have closed-form sizes, and comparing leftmost vs rightmost depth detects perfection in O(log n). The same 'measure two spines, formula or split' idea is really a binary search for the last filled position.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Maximum Depth of Binary TreeThe spine-height helpers here are exactly a depth computation along one side.
- Balanced Binary TreeAlso reasons about subtree heights, but compares left vs right depth at every node for balance.
- Complete Binary Tree InserterComing soonMaintains completeness under insertion — leans on the same left-to-right fill property.
- Check Completeness of a Binary TreeComing soonVerifies the completeness property with a BFS that forbids a node after any null.