Check Completeness of a Binary Tree
Table of Contents + β
A complete binary tree is the shape behind heaps and array-based trees. So checking if a tree has that shape comes up a lot. The trick the interviewer wants to see is small. Once you find an empty spot, no real node is allowed to appear after it. Spot that rule and the problem is solved.
π― The Problem
You get a binary tree and must say whether it is complete.
The rules:
- A binary tree is a structure where each node has a value and up to two children.
- Complete means every level is fully filled, except maybe the last one.
- The last level fills from the left with no gaps in the middle.
- Return
trueif the tree follows this shape, otherwisefalse.
Input (complete): Input (not complete): 1 1 / \ / \ 2 3 2 3 / \ / / \ 4 5 6 4 7
Output: true Output: false
Explanation: Left tree fills every level, last level packed from the left. Right tree has a gap: 2 is missing a right child, but 3 still has a right child.Here is the complete tree. Notice the last level packs tight from the left with no holes.
π’ Approach 1: Number the Nodes by Index (Brute Force)
The idea in one line: store the tree as an array and check that the indices have no holes.
The idea:
- Give the root index
0. - A node at index
ihas its left child at2*i + 1and right child at2*i + 2. - This is the same math heaps use.
How it works:
- Walk the tree and hand out indices this way.
- In a complete tree with
nnodes, every index stays between0andn - 1. - Find the largest index used. If it equals
n - 1, the tree is complete.
Why it is weak:
- The indices can grow very large on a one-sided tree.
- Those big numbers can overflow on big inputs.
- It is correct but fragile, so we want something safer.
Here is the index-numbering code:
def is_complete_tree(root): nodes = [] def dfs(node, index): if node: nodes.append(index) dfs(node.left, 2 * index); dfs(node.right, 2 * index + 1) dfs(root, 1) return len(nodes) == max(nodes) if nodes else Trueβ‘ Approach 2: BFS With a Null Flag (Best)
The idea in one line: read the tree row by row, and the moment you pass an empty spot, no real node may come after it.
The idea:
- Use BFS, short for breadth-first search, which reads each full row before the next using a queue.
- Do not skip missing children. Push
nullinto the queue when a child is absent. - A
nulljust marks an empty spot.
How it works:
- Keep one flag
seen_null, starting false. - Pop items off the queue one by one.
- When you pop a
null, set the flag to true. - After the flag is true, if you pop a real node, the tree is not complete.
Why it is fast:
- A real node after an empty spot is exactly the gap a complete tree forbids.
- One pass over the nodes and one flag answer the whole question.
- No huge index numbers, so no overflow.
This diagram shows the flag flipping when the first empty spot appears, and how any later real node fails the check.
Steps to Solve
- If the tree is empty, it counts as complete.
- Put the root in a queue. Set a flag
seen_nullto false. - While the queue is not empty, pop the front item.
- If the item is
null, setseen_nullto true and continue. - If the item is a real node but
seen_nullis already true, return false. - Otherwise push the nodeβs left child, then its right child, even when they are
null. - If the loop finishes without failing, return true.
This Python version uses a deque and stores None for missing children, checking the flag as each item leaves the queue.
from collections import deque
class Node: def __init__(self, val): self.val = val self.left = None self.right = None
def is_complete(root): if not root: return True queue = deque([root]) seen_null = False # have we passed an empty spot yet? while queue: cur = queue.popleft() if cur is None: seen_null = True # mark the first gap else: if seen_null: return False # a real node after a gap means not complete queue.append(cur.left) # push children, even if None queue.append(cur.right) return True
root = Node(1)root.left = Node(2)root.right = Node(3)root.left.left = Node(4)root.left.right = Node(5)root.right.left = Node(6)print(is_complete(root))The output of the above code will be:
TrueLet us read the Python version line by line, since the flag rule is the whole answer.
The line if not root: return True treats an empty tree as complete. There is nothing to break the rule, so it passes.
The line queue = deque([root]) starts the BFS with the root. A deque lets us pop from the front quickly.
The line seen_null = False sets the flag. It asks one question: have we already passed an empty spot? At the start, no.
The line cur = queue.popleft() takes the next item from the front. Items come out in level order, top row before bottom row, left before right.
The branch if cur is None: seen_null = True handles an empty spot. We do not stop here. We just remember that a gap happened.
The branch if seen_null: return False is the real check. If a real node shows up while the flag is already true, a node came after a gap. A complete tree never allows that. So we fail right away.
The two queue.append lines push both children, even when they are None. Pushing the empty spots on purpose is what lets the flag catch a gap. If we skipped None children, we could never see the gap.
The final return True runs only when the whole queue drained without a real node ever following a gap. That means the tree is complete.
β±οΈ Time and Space Complexity
The index way walks every node once, so it is O(n), but its indices can grow huge and overflow on awkward trees. The BFS flag way also visits each node once and pushes at most a few extra null markers. So it is a clean O(n) with no overflow worry. Both keep a queue, so both use O(n) memory in the worst case.
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Index numbering check | O(n) | O(n) |
| BFS with null flag | O(n) | O(n) |
Tip
The one rule to state clearly is this. Once you see an empty spot in level order, no real node may appear after it. That single sentence is the whole completeness test.
π§© Key Takeaways
- β A complete tree fills every level, and the last level packs from the left with no gaps.
- β
Read the tree in level order with BFS, pushing empty spots as
nulltoo. - β
Keep a flag that turns true the first time you pop a
null. - β After the flag is true, any real node means the tree is not complete.
- β The whole check is one pass and runs in O(n) without index overflow.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What shape does a complete binary tree have?
Why: Complete means all levels are full except possibly the last, which packs from the left.
- 2
Why do we push null children into the queue in the BFS approach?
Why: Pushing nulls lets us notice the first gap; without them we could never see it in the queue.
- 3
After the seen-null flag is true, what makes the tree not complete?
Why: A real node appearing after a gap breaks the left-packed rule, so the tree is not complete.
- 4
What is the time complexity of the BFS null-flag check?
Why: Each node and a few null markers are visited once, so the check runs in linear time.