On this page
Description
Level views answer questions about a tree one row at a time. Print it level by level, zigzag between directions, show what's visible from the left or right side, or find the widest level. Breadth-first search with a queue handles all of them. Snapshot the queue's size at the start of each level, and the loop knows exactly where one level ends.
We'll go from simple to more technical:
- The pattern: BFS with a level-size snapshot.
- A second flavor: DFS with an explicit depth (left and right views).
- Measuring width with position indices.
- The bugs that blur levels together, and BFS versus DFS memory.
The approach
A recursive DFS jumps between levels all the time, so it has no natural moment when "this level is done". A breadth-first walk with a queue visits the tree level by level. The one trick: at the start of each level, the queue holds exactly that level's nodes. Read the queue's size then, and process exactly that many nodes. Their children, added along the way, wait for the next round.
| Level | Queue at the start | First node (left view) | Last node (right view) |
|---|---|---|---|
| 0 | 7 | 7 | 7 |
| 1 | 3, 9 | 3 | 9 |
| 2 | 2, 8, 11 | 2 | 11 |
| 3 | 4 | 4 | 4 |
The right view is 7, 9, 11, 4. Notice that 4 hangs on the left side but is still the rightmost node on its level.
Java
Queue<TreeNode> queue = new ArrayDeque<>();
if (root != null) queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size(); // exactly the nodes at the current level
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (i == 0) { /* leftmost of this level - left view */ }
if (i == size - 1) { /* rightmost of this level - right view */ }
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
Python
from collections import deque
queue = deque([root] if root else [])
while queue:
size = len(queue) # exactly the nodes at the current level
for i in range(size):
node = queue.popleft()
if i == 0:
pass # leftmost of this level - left view
if i == size - 1:
pass # rightmost of this level - right view
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
Each question here is this loop plus one choice of what to keep per level. That's the first node, the last, the whole level (reversed every other time for zigzag), or the span of positions.
A second flavor: DFS with an explicit depth
Level questions don't strictly need a queue. A DFS can carry the current depth, and the first node it reaches at each new depth is the view from one side. It just has to visit that side's child first:
Java
void rightView(TreeNode node, int depth, List<Integer> out) {
if (node == null) return;
if (depth == out.size()) out.add(node.val); // first node reached at this depth
rightView(node.right, depth + 1, out); // right first, so the rightmost node wins
rightView(node.left, depth + 1, out);
}
Python
def right_view(node: TreeNode | None, depth: int, out: list[int]) -> None:
if node is None:
return
if depth == len(out):
out.append(node.val) # first node reached at this depth
right_view(node.right, depth + 1, out) # right first, so the rightmost node wins
right_view(node.left, depth + 1, out)
On the tree above it also returns 7, 9, 11, 4. But DFS order isn't level order. Questions that need true top-to-bottom order, like the top and bottom views (see Binary Tree Vertical Ordering), need explicit depth tracking or BFS.
Measuring width with position indices
The maximum width counts gaps too. Number the nodes like a heap: the children of position i are 2i + 1 and 2i + 2. A level's width is its last position minus its first, plus one. In the tree above, level 2 has a gap where 3's right child would be, so its width is 4. Raw positions double every level, though, and overflow on deep trees. So the queue carries (node, position) pairs, and each node's position is reduced by its level's first position before its children's are computed. No width changes, and the numbers stay small:
Java
long idx = entry.index - first; // normalized: 0 .. width - 1
if (node.left != null) queue.offer(new Entry(node.left, 2 * idx + 1));
if (node.right != null) queue.offer(new Entry(node.right, 2 * idx + 2));
Python
idx = index - first # normalized: 0 .. width - 1
if node.left:
queue.append((node.left, 2 * idx + 1))
if node.right:
queue.append((node.right, 2 * idx + 2))
Where you'll see it
Drawing an org chart one row at a time uses exactly this loop. So does working out how wide a layout gets at its widest row.
Which tool for which job?
| You need | Use |
|---|---|
| Nodes level by level (or zigzag) | BFS, snapshotting the queue size per level |
| Left or right view | BFS (first or last per level), or DFS recording the first node per depth |
| Top or bottom view | BFS with a column per node (the shallowest or last per column) |
| The widest level, counting gaps | BFS with position indices, normalized per level |
| Little memory to spare | BFS needs O(width), DFS O(height): pick by the tree's shape |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
Re-reading queue.size() in the loop | levels merge | read the size once per level |
| Top view by DFS without depth | a deeper node wins a column | BFS, or keep the shallowest depth per column |
| Raw position indices on deep trees | they double each level (overflow, or huge ints) | subtract the level's first index |
list.pop(0) as a Python queue | each pop shifts the list - O(n^2) | collections.deque |
| Reversing a zigzag level in place in the queue | later levels come out in the wrong order | reverse only the output list |
Check yourself
Try to answer before opening each one - recalling beats re-reading.
1. A level-order loop is written for (int i = 0; i < queue.size(); i++). What does it print for the tree [1, 2, 3]?
Show answer
1 2 on the first line and 3 on the second. The size is re-read every time round. After 1 is taken out, its children 2 and 3 are in the queue, so the loop goes on and pulls 2 into the first "level". Reading the size once, before the loop, fixes the level at one node.
2. A right-view DFS records the first node per depth but visits the left child first. What does it return for [1, 2, 3]?
Show answer
1 2, which is the left view. The first node reached at each depth is from whichever side is visited first. The rule is right; the visiting order decides which side it shows.
3. Why does maximum width subtract each level's first position before computing the children's positions?
Show answer
Raw positions double every level and overflow on deep trees. Width only depends on differences within a level, so subtracting the first position changes no width and keeps the numbers small.
Key takeaways
- Read the queue's size before each level's loop. It's the only thing that marks where a level ends.
- Level questions differ only in what they keep per level: the first node, the last, the whole level, or the span of positions.
- A DFS with an explicit depth can find the side views in O(height) space, but its order isn't level order.
- Heap-style positions measure width including gaps. Normalize them per level.