LeetCode #102 Medium

Binary Tree Level Order Traversal

Binary Tree Level Order Traversal is LeetCode 102 (Medium). The level order traversal of binary tree asks you to return its node values level by level, each level as its own list, read left to right.

  • The first list holds the root, the second its children, and so on down the tree.
  • Within a level the order is left to right, which is the order a queue hands the nodes back.
  • An empty tree has no levels, so the answer is an empty list.

The tree holds up to 2000 nodes, so a single traversal that touches each node once is all the problem needs.

Constraints
  • The number of nodes in the tree is in the range [0, 2000].
  • -1000 <= Node.val <= 1000
treebfsqueue
Open on LeetCode ↗
02

Intuition

BFS binary tree level by level works because a queue hands nodes back in the order they arrived, and children are pushed while their parents are being read, so the queue always holds the rest of the current level followed by the start of the next one. The two groups sit side by side with nothing marking the boundary.

The boundary is the queue's length at the moment the level begins. Whatever is in the queue right then is exactly this level; everything pushed afterwards belongs to the next one. So take that count first, pop exactly that many nodes, and the level separates itself.

How to spot this pattern

Any question phrased per level — level order, zigzag order, the right side view, the average of each row, the largest value per row, minimum depth — wants BFS with a queue rather than DFS. The one move that makes them all work is freezing the queue's size before draining it, because that count is the current level's width.

03

Approach

Try it first

Before reading on, run [3, 9, 20, null, null, 15, 7] through a queue by hand and write down what the queue holds just before each level starts. Then say which number tells you where level 1 ends. Aim for O(n) time.

1

Guard the empty tree, then seed the queue

Return an empty list straight away when root is null. Otherwise put the root in a queue, which is the whole of level 0. Skipping the guard queues None as if it were a node and the first node.val raises an error.

2

Freeze the queue's size at the top of each level

Before reading anything, record size = len(q). Those nodes are the complete current level, because every node pushed from here on is a child and belongs one level deeper. This single line is what keeps the levels apart.

3

Pop exactly that many nodes

Loop size times and, for each node popped from the front:

  • append its value to this level's list.
  • push any non-null children to the back.

The queue grows while you do it, which is fine, since the frozen count stops you from reading into the next level.

4

Store the level and repeat

Add the finished list to the results and go round again while the queue is non-empty. What is left in the queue is precisely the next level, already in left-to-right order, so the next snapshot works the same way.

04

Binary Tree Level Order Traversal solution in Python | C++ | Java

▶1from collections import deque
▶2 
▶3class Solution:
▶4 def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
▶5 if not root:
▶6 return []
▶7 res, q = [], deque([root])
▶8 while q:
▶9 size = len(q)
▶10 level = []
▶11 for _ in range(size):
▶12 node = q.popleft()
▶13 level.append(node.val)
▶14 if node.left:
▶15 q.append(node.left)
▶16 if node.right:
▶17 q.append(node.right)
▶18 res.append(level)
▶19 return res
3920157queue3levelsemptyqueue the root
queue3level 0
res[]no levels yet
Start. The root is the whole of level 0, so it goes into the queue alone. A queue hands nodes back in arrival order, which is left to right within a level, and that is the order the answer wants.
3920157queue3this level: 1levelsemptyfreeze size = 1
size1nodes in the queue now
level0about to be read
Level 0 begins. The queue holds 1 node right now, and they are exactly this level. Freezing that count is the whole trick: everything pushed from here on is a child and belongs to the next level, so the bracket marks where this level ends.
3920157queue920levels03push 9 and 20
pop31 of 1
level 0[3]so far
push9, 20next level
Pop 3 and record it, then push its children 9 and 20 onto the back of the queue. Those land behind the rest of this level, which is why the frozen count still bounds the row correctly.
3920157queue920levels03level 0 stored
level 0[3]complete
queue9, 20the next level
Level 0 is finished and stored as [3]. What remains in the queue is precisely level 1, already in left-to-right order, so the next round freezes its size the same way.
3920157queue920this level: 2levels03freeze size = 2
size2nodes in the queue now
level1about to be read
Level 1 begins. The queue holds 2 nodes right now, and they are exactly this level. Freezing that count is the whole trick: everything pushed from here on is a child and belongs to the next level, so the bracket marks where this level ends.
3920157queue20levels0319leaf: push nothing
pop91 of 2
level 1[9]so far
pushnoneleaf
Pop 9 and record it. It has no children, so nothing joins the queue and the next level simply stays as it is.
3920157queue157levels031920push 15 and 7
pop202 of 2
level 1[9, 20]so far
push15, 7next level
Pop 20 and record it, then push its children 15 and 7 onto the back of the queue. Those land behind the rest of this level, which is why the frozen count still bounds the row correctly.
3920157queue157levels031920level 1 stored
level 1[9, 20]complete
queue15, 7the next level
Level 1 is finished and stored as [9, 20]. What remains in the queue is precisely level 2, already in left-to-right order, so the next round freezes its size the same way.
3920157queue157this level: 2levels031920freeze size = 2
size2nodes in the queue now
level2about to be read
Level 2 begins. The queue holds 2 nodes right now, and they are exactly this level. Freezing that count is the whole trick: everything pushed from here on is a child and belongs to the next level, so the bracket marks where this level ends.
3920157queue7levels031920215leaf: push nothing
pop151 of 2
level 2[15]so far
pushnoneleaf
Pop 15 and record it. It has no children, so nothing joins the queue and the next level simply stays as it is.
3920157queueemptylevels0319202157leaf: push nothing
pop72 of 2
level 2[15, 7]so far
pushnoneleaf
Pop 7 and record it. It has no children, so nothing joins the queue and the next level simply stays as it is.
3920157queueemptylevels0319202157level 2 stored
level 2[15, 7]complete
queueemptynothing left
Level 2 is finished and stored as [15, 7]. The queue is empty, so there is no further level and the loop ends.
3920157queueemptylevels0319202157return 3 levels
answer[[3], [9,20], [15,7]]3 levels
Return [[3], [9,20], [15,7]]. Every node entered and left the queue exactly once, so the traversal is O(n), and the queue never held more than two neighbouring levels at a time.
05

Common pitfalls

Letting the loop bound re-read the growing queue

✗ Wrong
while (!q.isEmpty()) {
    List<Integer> level = new ArrayList<>();
    for (int i = 0; i < q.size(); i++) {
        ...
    }
}
✓ Right
while (!q.isEmpty()) {
    int size = q.size();
    List<Integer> level = new ArrayList<>();
    for (int i = 0; i < size; i++) {
        ...
    }
}

q.size() in the condition is re-evaluated every turn, and the queue is growing as children are pushed, so the loop swallows the next level too and the rows run together. Capture the count once before the loop.

Using a list and pop(0) as the 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 costs O(n) per call and turns the traversal into O(n²) on a wide tree. deque.popleft() is O(1).

Not guarding the empty tree

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

A null root is queued as though it were a real node, and the first node.val raises an error. The constraints allow a tree of 0 nodes, so this case is tested.

06

Edge cases

Empty tree, root = null

The guard returns an empty list before the queue is ever seeded.

A single node

One level with one value, [[1]]; the root's children are both null so nothing is pushed.

A tree skewed to one side

Each level holds exactly one node, giving n single-value lists.

07

Complexity

Time
O(n)
Space
O(w)
The binary tree level order traversal python code pushes and pops every node exactly once, so the time is linear in the node count. The queue holds at most two neighbouring levels, so the space is the width w of the widest level, up to about n/2 in a full tree.
08

Binary Tree Level Order Traversal FAQ

Can level order traversal be done with DFS instead of a queue?

Yes. Recurse carrying the depth, and append each value to res[depth], creating the row the first time that depth is reached. It gives the same answer in O(n), but the recursion stack is the tree's height and the queue version matches how the problem is phrased.

Why does the queue hold at most two levels at once?

While a level is being drained, only its own remaining nodes and the children pushed so far are in the queue. Once the level finishes, the queue holds exactly the next level, so it never spans three.