LeetCode #102 Medium

Binary Tree Level Order Traversal

Return node values level by level, each level its own list.

treebfsqueue
Open on LeetCode ↗
02

Intuition

A queue processes nodes in arrival order, and children arrive after parents — so the queue naturally holds one level's tail and the next level's head. Snapshot the queue length at each round: exactly that many pops belong to the current level.

How to spot this pattern

The moment a tree problem mentions levels — level order, right side view, zigzag, minimum depth, largest value per row — you want BFS with a queue, not DFS. The one trick that makes it work is snapshotting len(q) before draining: that count is exactly the current level's width, because everything added during the drain belongs to the next level.

03

Approach

1

BFS with a queue

Start with the root. Pop a node, record it, push its children — classic breadth-first.

2

Level boundary = queue length

Before each round, size = len(queue) is the count of the current level. Pop exactly size nodes into one sublist.

3

Children queue for the next round

Everything pushed during a round is the next level, processed in the next iteration.

04

Solution & live demo

1from collections import deque
2 
3class Solution:
4 def levelOrder(self, root):
5 if not root: return []
6 res, q = [], deque([root])
7 while q:
8 level = []
9 for _ in range(len(q)):
10 node = q.popleft()
11 level.append(node.val)
12 if node.left: q.append(node.left)
13 if node.right: q.append(node.right)
14 res.append(level)
15 return res
05

Common pitfalls

Reading the queue length inside the loop

✗ Wrong
while q:
    level = []
    for _ in range(len(q)):
        node = q.popleft()
        ...
✓ Right
while q:
    level = []
    size = len(q)
    for _ in range(size):
        ...

Python evaluates range(len(q)) once, so this specific form is safe — but it reads as though it re-checks, and translated literally into a while i < q.size() loop in another language it silently absorbs the next level's children into the current row. Capturing the size in a named variable makes the intent unambiguous.

Using a list and pop(0) as a queue

✗ Wrong
q = [root]
node = q.pop(0)
✓ Right
q = deque([root])
node = q.popleft()

list.pop(0) shifts every remaining element left, so it's O(n) per call and turns the traversal into O(n²). deque.popleft() is O(1).

Not handling an empty tree

✗ Wrong
res, q = [], deque([root])
✓ Right
if not root: return []
res, q = [], deque([root])

A None root gets queued as a real entry, then node.val raises AttributeError. Guard before the queue is seeded.

06

Edge cases

Empty tree

No rounds → [].

Skewed tree

Every level has one node → n singleton lists.

07

Complexity

Time
O(n)
Space
O(w)
w = maximum level width.