Binary Tree Level Order Traversal
Interview Prep
The problem
Return the values of a binary tree, grouped by level (root level is index 0). Output a list of lists.
class Node:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right Pattern: BFS with level snapshot
Standard BFS on a tree, with one critical detail: capture
len(q) before the inner loop starts.
That count is exactly the number of nodes at the current level;
after processing them you'll have grown the queue by their
children, which belong to the next level. Without that snapshot
you can't tell where one level ends and the next begins.
Solution: BFS
from collections import deque
def level_order(root: Node | None) -> list[list[int]]:
if root is None: return []
q, out = deque([root]), []
while q:
level = []
for _ in range(len(q)): # snapshot the level size FIRST
node = q.popleft()
level.append(node.val)
if node.left: q.append(node.left)
if node.right: q.append(node.right)
out.append(level)
return out Trace
Tree: 3
/ \
9 20
/ \
15 7
Level 0: q=[3]. level=[3]. push 9, 20. q=[9,20].
Level 1: q=[9, 20]. level=[9, 20]. push 15, 7. q=[15,7].
Level 2: q=[15, 7]. level=[15, 7]. no children. q=[].
End.
return [[3], [9, 20], [15, 7]] Alternative: DFS with a depth argument
Same answer, different traversal order. DFS visits the leftmost
branch all the way down first, but because we track depth
explicitly and append to out[depth], the output
is still grouped by level. Slightly less natural for this problem
but a useful template — many "answer per level" or
"tag with depth" problems reduce to this DFS form.
def level_order_dfs(root):
"""Same answer via DFS with explicit depth."""
out = []
def walk(node, depth):
if node is None: return
if depth == len(out): out.append([])
out[depth].append(node.val)
walk(node.left, depth + 1)
walk(node.right, depth + 1)
walk(root, 0)
return out Complexity
- Time:
O(n). - Space:
O(w)for the BFS queue, wherewis the maximum level width.O(h)for the DFS stack.
Variations worth knowing
- Right-side view: return the rightmost node at each level. BFS: take the last node of each level snapshot. DFS visiting right-child-first: the first node at each new depth is the answer.
- Zigzag traversal: alternate left-to-right and right-to-left levels. Either reverse every other level after the fact, or push to a deque from alternating ends.
- Bottom-up level order: same algorithm, reverse the final list.
- Average of each level / minimum at each level: identical skeleton, swap the per-level aggregate.
- Connect "next" pointers across levels: at each
level snapshot, set
node.nextto the next node in the queue. The classic "Populating Next Right Pointers" problem.