“Level by level” is the definition of breadth-first search — a queue naturally processes nodes in that order. The only trick is snapshotting the queue’s size before draining each level, so nodes from the next level (enqueued mid-loop) do not get mixed into the current one.
BFS (queue, level by level)
OptimalTime O(n)Space O(n)At the start of each iteration, len(queue) is exactly the number of nodes at the current level. Popping exactly that many, and appending their children, drains one full level per outer loop iteration.
from collections import deque
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right
class Solution: def levelOrder(self, root: TreeNode | None) -> list[list[int]]: if root is None: 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 resultTracing root = [3,9,20,null,null,15,7]:
Queue starts with just the root. Level 0: [3].
Complexity: every node is enqueued and dequeued exactly once, doing O(1) work each time → O(n) time. The queue holds at most one full level, which can be up to ~n/2 nodes in a wide tree → O(n) space (the output array is also O(n), which does not change the bound).
DFS (recursive, tracking depth)
Time O(n)Space O(h)BFS is not the only way: a pre-order DFS that carries its current depth can append directly into result[depth], creating a new sublist the first time a depth is reached.
class Solution: def levelOrder(self, root: TreeNode | None) -> list[list[int]]: result = []
def dfs(node: TreeNode | None, depth: int) -> None: if node is None: return if depth == len(result): result.append([]) result[depth].append(node.val) dfs(node.left, depth + 1) dfs(node.right, depth + 1)
dfs(root, 0) return resultWhy consider this: still O(n) time, and it trades the queue’s O(n) width for the recursion stack’s O(h) depth — a win for a wide, shallow tree, though worse than BFS for a deep, narrow one. It also visits nodes in a different order (root before its children, not strictly level by level in real time), which only matters if intermediate order is observed.