Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Standing on the right of the tree, you see exactly one node per level — the rightmost one at each depth. Two clean ways: BFS by level and keep the last node dequeued in each level, or DFS visiting right-before-left and recording the first node seen at each new depth. Both are O(n) time; DFS is O(h) space.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you realize the answer is the rightmost node per level (not the right spine), and whether you can reduce a level-order traversal to one value per level — via BFS-last or DFS-right-first.
Where candidates slip
Just walking down the right children, which fails when a left subtree extends deeper than the right one and contributes the visible node for those lower levels.
How to stand out
Call out the 'left node visible when right subtree is shorter' case with a concrete example, then give the DFS right-first solution in O(h) space and mention BFS as the equally valid alternative.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewRightmost node per levelPrefer iterative level handling?YesNoExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
BFS — Last Node of Each Level
1Hint 1Explore hintHide hint
If you traversed the tree level by level, which node in each level is the one you'd see from the right?
2Hint 2Explore hintHide hint
Run standard BFS with the queue-size snapshot. The last node you dequeue in a level is its rightmost.
3Hint 3Explore hintHide hint
During each level's loop, when the index reaches levelSize - 1 (or simply overwrite a variable each iteration), record that node's value.
A · Core idea
This builds directly on level-order traversal. We process the tree one level at a time; the visible node for a level is simply the last one we touch in that level.
Reusing the level-size snapshot. As in level-order traversal, we read levelSize = queue.size() before draining a level so children don't leak in. Within the level loop we don't need the whole row — we just remember the most recent node, and after the loop the last one remembered is the rightmost. (Enqueueing left-then-right means the last dequeued is the furthest right.)
Why the last node is the visible one. Because we enqueue children left before right at every step, each level enters the queue in left-to-right order. Draining the level in FIFO order means the final dequeued node sits furthest right — exactly what an observer on the right sees.
This handles the shorter-right-subtree case for free. We never assume the visible node descends from a right child. If only a left-subtree node exists at some depth, it's still the last (and only) node dequeued for that level, so it's correctly recorded.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
root = [1,2,3,null,5,null,4]level 0
drain [1] → last is 1
view=[1], queue=[2,3][1,3,4]C · Code with teaching comments
Reference implementation preview// Definition for a binary tree node. // class TreeNode { // int val; // TreeNode left; // TreeNode right;Explore the full codeHide the full code
D · Why it works and complexityO(n) timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
Right side view is 'level-order traversal, but collapse each level to its last element'. That reframing makes the trap (left node visible when the right subtree is shorter) disappear, because BFS-by-level doesn't care which subtree a node came from.
ComplexityO(n) time, O(n) space — Standard BFS visits each node once → O(n) time. The queue can hold a whole level → O(n) space in the worst case.
E · Edge cases and pitfalls3Recording the first node instead of the last (that would be a left-side view).Explore edge casesHide edge cases
Pitfalls
- Recording the first node instead of the last (that would be a left-side view).
- Re-reading queue size inside the loop, merging levels.
- Forgetting the empty-tree guard.
Approach 2 of 2 · Optimal
DFS Right-First — First Node Per Depth (Optimal Space)
1Hint 1Explore hintHide hint
If you recurse into the right child before the left child, which node at each depth do you reach first?
2Hint 2Explore hintHide hint
Carry a depth argument. The first time you arrive at a given depth, that node is the rightmost for that level.
3Hint 3Explore hintHide hint
Compare depth to the size of your result list: if they're equal, you've reached a new depth for the first time — append this node, then recurse right then left.
A · Core idea
Instead of materializing each level, we let recursion order do the selection. Visiting the right child first guarantees the first node we ever reach at a depth is the rightmost one.
Why right-before-left works. Pre-order DFS reaches a node before any of its descendants. If at each node we recurse into the right subtree first, then among all nodes at a given depth the one encountered earliest is the furthest right. So 'first node seen at depth d' equals 'rightmost node at depth d'.
The 'new depth' test. We keep view where view[d] is the rightmost node at depth d. When we descend to depth d and len(view) == d, it means we have not recorded depth d yet, so this node — the first arrival — is the rightmost. We append it. Every later node at depth d finds len(view) > d and is skipped.
Why this nails the trap case. Suppose the right subtree is shorter. At the deeper levels the right side has no nodes, so the first (and only) arrivals at those depths come from the left subtree — and the len(view) == depth test records them. We never assume the visible node is a right child.
Space win. DFS only keeps a root-to-leaf path on the stack, O(h), versus BFS holding a full level, O(width). For balanced trees the widest level is ~n/2, so DFS can be markedly leaner.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
root = [1,2,3,null,5,null,4]Interactive diagram
Why a left node can be visible
node 1, depth 0Right subtree of 1 (node 3) ends at depth 2 via node 4; node 5 under 2 is at the same depth but to its left, so 4 wins level 2.
[1,3,4]C · Code with teaching comments
D · Why it works and complexityO(n) timeO(h) spaceOpen the invariant and proofExplore the proofHide the proof
Order of recursion is a selection mechanism. By choosing right-before-left, 'first seen at this depth' becomes 'rightmost at this depth' with no comparisons. Swap to left-before-right and the identical skeleton produces the left side view.
ComplexityO(n) time, O(h) space — Each node is visited once → O(n). Only the recursion stack is extra, bounded by the tree height → O(h) space, better than BFS's O(width).
E · Edge cases and pitfalls6root = [1,2,3,4] (4 is left child of 2) — Right subtree (3) is childless; at depth 2 only node 4 exists, and DFS right-first records it → [1,3,4].Explore edge casesHide edge cases
Edge cases handled
root = [1,2,3,4] (4 is left child of 2)Right subtree (3) is childless; at depth 2 only node 4 exists, and DFS right-first records it → [1,3,4].Left-only spine [1,2,null,3]Each level has one node from the left chain; every one is the rightmost-by-default → [1,2,3].root = nulldfs returns immediately; view stays empty → [].
Pitfalls
- Recursing left before right — produces the LEFT side view.
- Using a 'visited depth' set incorrectly; the len(view) == depth test is simpler and correct.
- Assuming the answer is the right spine — fails when the left subtree is deeper.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code preview// Definition for a binary tree node. // class TreeNode { // int val; // TreeNode left; // TreeNode right;Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Following only right children
Walking down node.right misses levels where the right subtree is shorter than the left. On [1,2,3,4] the right child of root (3) has no children, but node 4 (left child of 2) is visible at depth 2.
Explore the code comparisonHide the code comparison
Following only right children
Walking down node.right misses levels where the right subtree is shorter than the left. On [1,2,3,4] the right child of root (3) has no children, but node 4 (left child of 2) is visible at depth 2.
Don't write this
node, view = root, []
while node: # only the right spine
view.append(node.val)
node = node.rightWrite this instead
def dfs(node, depth):
if not node: return
if depth == len(view):
view.append(node.val)
dfs(node.right, depth + 1)
dfs(node.left, depth + 1)2DFS in the wrong child order
If you recurse left before right, the first node recorded at each depth is the leftmost, so you build the left side view instead of the right.
Explore the code comparisonHide the code comparison
DFS in the wrong child order
If you recurse left before right, the first node recorded at each depth is the leftmost, so you build the left side view instead of the right.
Don't write this
if (depth == view.size()) view.add(node.val);
dfs(node.left, depth + 1, view); // left first → left view
dfs(node.right, depth + 1, view);Write this instead
if (depth == view.size()) view.add(node.val);
dfs(node.right, depth + 1, view); // right first → right view
dfs(node.left, depth + 1, view);Revise
Carry the pattern into the interview
Pattern to remember
Per-level extremum (rightmost per depth)
Code pattern to memorize
# DFS, right-first
def dfs(node, depth):
if not node: return
if depth == len(view): view.append(node.val) # first at this depth = rightmost
dfs(node.right, depth + 1)
dfs(node.left, depth + 1)Rules to remember
- 1The answer is one value per level — the rightmost node at each depth, NOT the right-edge/right-spine of the tree.
- 2A left subtree node can be visible if the right subtree is shorter — don't just follow right children.
- 3BFS: take the last node you dequeue in each level. DFS: visit right before left and record the first node at each new depth.
- 4Track depth so you record exactly one value per level.
- 5Empty tree → empty list; guard the null root.
Use this pattern when
- You need one representative per level (rightmost, leftmost, max, min, average)
- 'What's visible from one side', 'last/first in each row' style questions
- Any level-grouped problem where you collapse each level to a single value
- Right-side / left-side view, largest value per row, bottom-up views
Choose another pattern when
- You need all nodes per level → full level-order traversal instead
- The tree is a pure right spine and you mistakenly think that's always the answer → it isn't when left subtrees go deeper
- You need the boundary/outline of the tree → that's the Boundary of Binary Tree problem, different logic
Interview takeaway
The right side view is the rightmost node of each level. Whether you BFS and grab the last per level or DFS right-first and grab the first per depth, the unifying idea is 'collapse each level to one representative'. The visible-from-shorter-left-subtree case is the trap that separates a real understanding from pattern-matching on right children.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternPer-level extremum. Collapse each level of a tree to a single representative — rightmost, leftmost, max, or average. The DFS-order-as-selection trick (right-first → rightmost) and the BFS last-per-level trick both reduce to 'one value per depth'. Same family as Largest Value in Each Row and level averages.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Binary Tree Level Order TraversalThe parent pattern — keep every node per level instead of just the last.
- Find Largest Value in Each Tree RowComing soonSame per-level collapse, but take the max of each level instead of the rightmost.
- Populating Next Right Pointers in Each NodeComing soonLevel-by-level processing where you link each node to its right neighbour.
- Binary Tree Vertical Order TraversalComing soonGroup by horizontal column instead of depth — BFS carrying a column index.