102 - Binary Tree Level Order Traversal
Difficulty: Medium | Pattern: BFS / Queue | Company tags: Amazon, Google, Facebook, Microsoft
Problem Statement
Given the root of a binary tree, return the level order traversal of its nodes' values (i.e., from left to right, level by level).
Example 1:
3
/ \
9 20
/ \
15 7
Input: root = [3, 9, 20, null, null, 15, 7]
Output: [[3], [9, 20], [15, 7]]
Example 2:
Input: root = [1]
Output: [[1]]
Example 3:
Input: root = []
Output: []
Algorithm Flow
Approach: BFS with Queue
Key insight: Use a queue (FIFO). Process the queue level by level: at the start of each level, record how many nodes are currently in the queue (that's this level's size), process exactly that many nodes, and add their children for the next level.
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def levelOrder(root) -> list[list[int]]:
if not root:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue)
level = []
for _ in range(level_size):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
Dry Run
Tree: root=3, left=9, right=20, 20.left=15, 20.right=7
| Iteration | queue before | level_size | level processed | queue after |
|---|---|---|---|---|
| 1 | [3] | 1 | [3] | [9, 20] |
| 2 | [9, 20] | 2 | [9, 20] | [15, 7] |
| 3 | [15, 7] | 2 | [15, 7] | [] |
Result: [[3], [9, 20], [15, 7]] ✓
Key Design: Snapshot the Level Size
The trick is level_size = len(queue) before the inner loop. This ensures we only process nodes from the current level — not nodes added to the queue as children during this iteration.
Variants
| Problem | Variation |
|---|---|
| 107 - Level Order Traversal II | Return levels bottom-up: result.reverse() at the end |
| 103 - Zigzag Level Order | Alternate left-to-right and right-to-left per level: use a flag |
| 637 - Average of Levels | Return average of each level instead of all values |
| 199 - Binary Tree Right Side View | Return only the last element of each level |
| 515 - Find Largest Value in Each Tree Row | Return max of each level |
Edge Cases
- Empty tree →
[] - Single node →
[[root.val]] - Skewed tree (all left or all right) → n levels each with 1 element
Complexity
- Time: O(n) — every node is enqueued and dequeued exactly once
- Space: O(w) where w is the maximum width of the tree; worst case O(n) for a complete tree's bottom level
Key Terms
| Term | Definition |
|---|---|
| BFS (Breadth-First Search) | Traversal strategy that visits nodes level by level using a FIFO queue. |
| Queue | FIFO data structure holding nodes waiting to be processed; children are enqueued after their parent is dequeued. |
| Level size snapshot | Capturing len(queue) before the inner loop so only the current level's nodes are processed. |
| Tree width | The maximum number of nodes at any single level, which bounds the queue's peak size. |
FAQ
Q: Can this be solved without extra space beyond the output? A: Not with a truly O(1) auxiliary approach — you need at least a queue (BFS) or a depth-tracking structure (recursive DFS) to group nodes by level; both use O(w) or O(h) extra space.
Q: What if the tree is empty?
A: The initial if not root: return [] check handles it directly, returning an empty list of levels.
Q: How would you solve this recursively (DFS) instead of BFS?
A: Pass the current depth into a helper; if depth == len(result), append a new list, then append node.val to result[depth], and recurse into both children.
Q: What's the difference between this and zigzag level order traversal (LC 103)? A: Same BFS skeleton, but alternate the direction each level is appended (or reverse odd-indexed levels) using a boolean flag.
Q: Why snapshot level_size = len(queue) before the inner loop instead of checking queue length inside the loop?
A: Because children are appended to the same queue during the inner loop; without snapshotting the size first, the loop would incorrectly consume next-level nodes as part of the current level.
Quick Revision
- Use a queue and process nodes in FIFO order to guarantee left-to-right, top-to-bottom visiting.
- Snapshot
level_size = len(queue)before each inner loop — this is the key trick that separates levels. - For each node dequeued, record its value and enqueue its non-null children.
- Append each completed level's values as one sub-list to the result.
- Empty tree → return
[]immediately. - Time: O(n), every node enqueued/dequeued once. Space: O(w), bounded by the widest level.
- Variants swap only the per-level logic: reverse order, alternate direction, take average, take last, take max.
- Can also be done recursively by tracking depth and appending to
result[depth].
Related Problems
- 107 - Binary Tree Level Order Traversal II (pattern: same BFS, reverse result at the end) — not in this directory.
- 103 - Binary Tree Zigzag Level Order Traversal (pattern: same BFS, alternate direction per level) — not in this directory.
- 637 - Average of Levels in Binary Tree — same BFS grouping, averages each level instead of collecting all values.
- 199 - Binary Tree Right Side View — same BFS grouping, keeps only the last node of each level.