datarekha
Coding Patterns Medium Asked at AmazonAsked at GoogleAsked at Meta

Return the level-order (BFS) traversal of a binary tree as a list of lists, one per level.

The short answer

Use a queue (deque) to process nodes layer by layer. At each step, snapshot the current queue length to know exactly how many nodes belong to the current level, drain those, then enqueue their children. The result is a list of lists without any depth-tracking variable.

How to think about it

The real test here is whether you can group nodes by level without carrying a depth counter around. Plenty of candidates reach for a recursive DFS that appends to result[depth]; it works, but it tells the interviewer you are tracking depth by hand. The cleaner answer leans on one fact about a queue — and the whole trick is a single len(queue) read.

The cue to hear is “process level by level” or “shortest path in an unweighted graph”: minimum depth, right-side view, zigzag. All of them want BFS, because a queue naturally holds exactly the nodes at the current frontier. The move that makes it click is reading the queue’s length before you start draining it. That count is precisely how many nodes sit on this level. You pop that many, collect their values into one list, enqueue their children as you go, and repeat until the queue empties — no depth variable anywhere.

A worked example

from collections import deque

class TreeNode:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def level_order(root):
    if not root:
        return []
    out, q = [], 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)                 # one finished level
    return out

# tree:   1
#        / \
#       2   3
#            \
#             4
root = TreeNode(1, TreeNode(2), TreeNode(3, None, TreeNode(4)))
print(level_order(root))           # three levels
print(level_order(TreeNode(42)))   # single node
print(level_order(None))           # empty tree
[[1], [2, 3], [4]]
[[42]]
[]

Trace the first line. The outer pass snapshots len(q) == 1, drains the root, and enqueues 2 and 3 — giving [1]. The next pass reads len(q) == 2, so it drains exactly those two and no further, enqueuing 3’s child 4 along the way — giving [2, 3]. The final pass reads 1, drains 4, and stops — giving [4]. The snapshot is what keeps 4 from leaking into the [2, 3] group. The single-node and empty cases confirm the two boundaries: one node returns one one-element level, an empty tree returns [].

Keep practising

All Coding Patterns questions

Explore further

Skip to content