Approach Summary
BFS with a queue. At each level, process all nodes in the queue (current level size), then enqueue their children.
Full Solution & Approach
Level-order traversal is BFS with the extra structure that each level is returned as its own list. Use a queue seeded with the root. While the queue is non-empty, snapshot its current length — that is the number of nodes in this level — and pop exactly that many nodes, collecting their values and enqueuing their children. The snapshot is the key: without it, you cannot tell where one level ends and the next begins, because children are appended to the same queue while you work. Each node is visited once and each edge once, so the total work is O(n). This is the canonical BFS-on-tree pattern; the same level-snapshot trick powers right-side-view (take the last node of each level), zig-zag order (reverse every other level), and minimum-depth BFS.
Every node is enqueued and dequeued exactly once — O(n) time. The queue holds at most the widest level — O(w) space where w is the tree width.
Solution Code
Solution
from collections import deque
def level_order(root):
if not root:
return []
result = []
queue = deque([root])
while queue:
level = []
for _ in range(len(queue)):
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 resultfunction levelOrder(root) {
if (!root) return [];
const result = [];
let queue = [root];
while (queue.length) {
const level = [];
const next = [];
for (const node of queue) {
level.push(node.val);
if (node.left) next.push(node.left);
if (node.right) next.push(node.right);
}
result.push(level);
queue = next;
}
return result;
}public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Deque<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()) {
List<Integer> level = new ArrayList<>();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(level);
}
return result;
}vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
if (!root) return result;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
vector<int> level;
int size = q.size();
for (int i = 0; i < size; i++) {
TreeNode* node = q.front(); q.pop();
level.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
result.push_back(level);
}
return result;
} Edge Cases to Watch
- Empty tree — return []
- Single node — [[value]]
- Skewed tree — each level has one node
- A full level of null children — they are simply not enqueued
How to Recognize This Pattern
- Level-by-level tree output
Complexity Analysis
Time Complexity
O(n)
Space Complexity
O(n)