Core Pattern → Pattern 15
Tree Breadth First Search
One queue. One template. Snapshot the level size before processing. Every tree level-order problem is a variation on the same 6-line skeleton.
Frequency:★★★★★
·~25 min·Beginner–Intermediate·9 problemsThe Core Shock
DFS with depth tracking for level problems — awkward. BFS with a queue — natural
DFS + depth param
O(n) but messy
Recursive, out-of-order, harder to extend for level ops
BFS + queue
O(n) · clean
Level-by-level, one template covers all variations
The insight: a queue processes nodes in exactly the order they should appear — level by level, left to right. Snapshot level_size = len(queue) before processing each level — that's the boundary between levels. One template, nine problems.
The Universal Template
Memorize these 6 lines. Every Tree BFS problem starts here.
bfs_template.pyO(n) time · O(n) space1from collections import deque23def bfs_template(root):4 if not root: return []5 queue = deque([root])6 result = []78 while queue: # while nodes remain9 level_size = len(queue) # <- SNAPSHOT: current level boundary10 current_level = []1112 for _ in range(level_size): # process exactly this level13 node = queue.popleft()14 current_level.append(node.val)15 if node.left: queue.append(node.left)16 if node.right: queue.append(node.right)1718 result.append(current_level) # save completed level1920 return result
The level_size snapshot is the entire trick. After you call
level_size = len(queue), the inner for-loop processes exactly those nodes — children are added to the queue but aren't counted. Without this snapshot, you lose level boundaries entirely.All Variations From One Template
One template, six variations — each adds exactly one line to the skeleton.
Level Order
Append current_level to result
Reverse Level Order
result.insert(0, level) OR reverse at end
Zigzag Traversal
deque + left_to_right flag + appendleft
Level Averages
sum(level) / len(level) per level
Minimum Depth
Return level when first leaf found (no children)
Right View
Take last node of each level: current_level[-1]
Connect Level Siblings
prev.next = node; track prev per level
Connect All Siblings
prev.next = node; no level boundary check
Pattern Recognition
BFS vs DFS — know which to reach for.
Use Tree BFS When
"Level order", "level by level", "level averages"
"Minimum depth" or "closest" leaf/node
"Right view" or "left view" of tree
"Connect nodes at same level"
"Zigzag" or "spiral" traversal
"Level order successor" of a node
"Shortest path" in unweighted tree/graph
Any problem needing level-boundary awareness
Common Pitfalls
Using queue.pop() (LIFO) instead of popleft() (FIFO)
Not snapshotting level_size — loses level boundaries
Using list.pop(0) instead of deque — O(n) per pop
Min depth: returning on first node with one null child (must be leaf)
Right view: recording first node not last per level
Connect siblings: not handling level boundaries (prev from prev level)
Forgetting 'if not root: return []' null check
Zigzag: using list.insert(0) instead of deque.appendleft
On this page