~25 minutes · Day 34 + Day 39 · tree BFS · Python
Tonight's interview skill is learning to read a tree one row at a time. Binary Tree Level Order Traversal asks for values grouped by depth: root first, then its children, then their children. That shape shows up whenever the question says level, row, nearest, or right side.
A queue processes nodes in the order discovered; snapshot the queue length to process exactly one level at a time.
That one sentence is the whole pattern. Depth-first search dives down a path. Breadth-first search, usually shortened to BFS, spreads outward level by level. A queue gives BFS its order: first discovered, first processed.
The slow approach is to collect depth 0, then start again from the root to collect depth 1, then start again for depth 2, and so on. That is O(n · h) time for n nodes and tree height h; in a skinny tree, h = n, so this becomes O(n²). It repeats the same walk just to know where one level ends.
The better question is: while we are already walking the tree, can we know the current level boundary? Yes: before processing a level, freeze len(queue). Those nodes are exactly the current level; children appended during the loop belong to the next level.
Queue snapshot: process only the nodes already in the queue; newly appended children form the next row.
Use collections.deque for the queue. It supports appending on the right and popping from the left efficiently; a normal list's pop(0) has to shift the remaining items (Python wiki: time complexity).
def level_order(root):
if not root: return []
ans, q = [], deque([root]) # discovered, not processed
while q:
level = []
for _ in range(len(q)): # snapshot this level only
node = q.popleft()
level.append(node.val)
if node.left: q.append(node.left)
if node.right: q.append(node.right)
ans.append(level)
return ans
The line for _ in range(len(q)) is doing the important work. Without that snapshot, the loop would keep eating newly added children and blur all levels into one long list.
Binary Tree Right Side View: return the node values visible if you stand on the right side of the tree.
Before opening the reveal: predict the move. If level order gives you every row, which value from each row should the right-side view keep?
Keep the last node processed in each level. The queue invariant does not change; only the value you save changes.
Right-side view uses the same level boundary; it only changes which value each level keeps.
def right_side_view(root):
if not root: return []
ans, q = [], deque([root])
while q:
size = len(q) # one level's boundary
for i in range(size):
node = q.popleft()
if i == size - 1: ans.append(node.val)
if node.left: q.append(node.left)
if node.right: q.append(node.right)
return ans
| Approach | Time | Extra space |
|---|---|---|
| Brute force (scan again for each depth) | O(n · h), worst O(n²) | O(h) |
| BFS levels (queue snapshot) | O(n) | O(w) |
Here w is the widest level. The returned answer itself can hold O(n) values, but the working queue only needs the current frontier.
1. What does the queue contain during BFS?
2. What keeps one level separate from the next?
len(q) before the level loop starts. Children appended during that loop wait in the queue for the next level.3. Spot the bug in this level loop:
level = []
while q:
node = q.popleft()
level.append(node.val)
if node.left: q.append(node.left)
if node.right: q.append(node.right)
ans.append(level)
while q drains the queue, including children that were just appended. A level loop needs a fixed count from the queue length snapshot.4. Which problem is the same BFS-level pattern?
Solve these on LeetCode, in order, logging each in your Notion tracker with the cycle — predict → attempt → compare → encode:
For the encode step, write the invariant at the top of this page in your own words. Revision day should test that sentence, not the exact code.
Watch NeetCode — Binary Tree Level Order Traversal after your attempt, as the compare step. Find the exact moment where his level-boundary reasoning matches or corrects yours.