Facebook Pixel

199. Binary Tree Right Side View

Problem Description

You are given the root of a binary tree. Imagine you are standing on the right side of the tree and looking at it from that perspective. Your task is to return the values of all nodes that would be visible to you from this right-side view, ordered from top to bottom.

When viewing a binary tree from the right side, you can only see the rightmost node at each level of the tree. Nodes that are to the left of other nodes at the same level would be hidden from your view.

For example, if you have a tree like:

      1
     / \
    2   3
     \   
      5   

From the right side, you would see the nodes [1, 3, 5]:

  • Level 1: Node 1 is visible (it's the root)
  • Level 2: Node 3 is visible (node 2 is hidden behind it)
  • Level 3: Node 5 is visible (it's the only node at this level)

The solution uses a level-order traversal (BFS) approach with a queue. For each level, it processes nodes from right to left by checking the right child before the left child. The first node encountered at each level (which would be the rightmost due to the traversal order) is added to the result list. The algorithm continues until all levels have been processed, returning the list of visible nodes from top to bottom.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • Yes: A binary tree is a special type of graph where each node has at most two children and there are no cycles.

Is it a tree?

  • Yes: The problem explicitly states we're working with a binary tree structure.

DFS

  • We arrive at DFS as the recommended approach for tree problems.

Conclusion: The flowchart suggests using DFS (Depth-First Search) for this binary tree problem.

Why DFS Works for Right Side View

While the provided solution uses BFS, DFS is equally effective for solving the Binary Tree Right Side View problem. Here's how DFS would approach it:

  1. Track depth during traversal: As we traverse the tree using DFS, we keep track of the current depth/level.

  2. Visit right children first: To ensure we see the rightmost node at each level first, we can traverse the right subtree before the left subtree.

  3. Record first node at each level: Since we're visiting right nodes first, the first node we encounter at each depth level is the rightmost node - exactly what we want for the right side view.

The DFS approach would traverse the tree recursively, maintaining a result list where we add the first node value encountered at each new depth level. This gives us the same right side view as the BFS approach, just using a different traversal strategy.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we think about viewing a tree from the right side, we need to identify which nodes would be visible. The key insight is that only one node per level can be seen - the rightmost one. Any nodes to its left at the same level would be blocked from view.

This immediately suggests we need to process the tree level by level. For each level, we want to identify and record the rightmost node. There are two main ways to traverse a tree level by level:

  1. BFS (Breadth-First Search): Naturally processes nodes level by level using a queue. We can process all nodes at a level together and pick the rightmost one.

  2. DFS (Depth-First Search): While DFS typically goes deep into the tree first, we can still track which level we're at during traversal. By visiting the right child before the left child, we ensure we encounter the rightmost node at each level first.

The BFS approach feels more intuitive here because:

  • We explicitly process one level at a time
  • We can easily identify which node is rightmost by looking at the order in the queue
  • By adding right children before left children to the queue, the first node we process at each level is guaranteed to be the rightmost

The trick in the provided solution is clever: instead of tracking the last node in each level (which would be rightmost if we add left children before right), it reverses the order - adding right children first. This way, the first node (q[0]) at the start of processing each level is the rightmost node. This small optimization makes the code cleaner as we don't need to wait until we've processed all nodes at a level to identify the rightmost one.

Solution Approach

The solution implements a BFS (Breadth-First Search) approach using a queue data structure. Here's how the algorithm works step by step:

  1. Initialize the result list and handle edge case:

    • Create an empty list ans to store the rightmost nodes
    • If the root is None, return the empty list immediately
  2. Set up the queue for BFS:

    • Use a deque (double-ended queue) for efficient operations
    • Initialize it with the root node: q = deque([root])
  3. Process nodes level by level:

    • The while q: loop continues as long as there are nodes to process
    • At the start of each level, q contains all nodes from that level
  4. Record the rightmost node of each level:

    • ans.append(q[0].val) adds the value of the first node in the queue
    • This works because of how we add children to the queue (right before left)
  5. Process all nodes at the current level:

    • for _ in range(len(q)): ensures we process exactly the nodes at this level
    • len(q) at the start of the loop gives us the count of nodes at the current level
  6. Add children in right-to-left order:

    • For each node, we popleft() to remove it from the queue
    • Check and add the right child first: if node.right: q.append(node.right)
    • Then check and add the left child: if node.left: q.append(node.left)
    • This ordering ensures that at the next level, the rightmost node appears first in the queue

The key insight is that by adding children in right-to-left order, the queue always has the rightmost node of each level at position q[0] when we start processing that level. This eliminates the need to track which node is last at each level.

Time Complexity: O(n) where n is the number of nodes, as we visit each node exactly once.

Space Complexity: O(w) where w is the maximum width of the tree, as the queue holds at most one level of nodes at a time.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the algorithm with a concrete example:

Tree:
      1
     / \
    2   3
   / \   
  4   5   

Initial State:

  • ans = []
  • q = deque([1])

Level 1 (root level):

  • Queue at start: [1]
  • Add q[0].val = 1 to ans → ans = [1]
  • Process 1 node (len(q) = 1):
    • Pop node 1
    • Add right child (3) → q = [3]
    • Add left child (2) → q = [3, 2]

Level 2:

  • Queue at start: [3, 2]
  • Add q[0].val = 3 to ans → ans = [1, 3]
  • Process 2 nodes (len(q) = 2):
    • Pop node 3
      • No right child
      • No left child
    • Pop node 2
      • Add right child (5) → q = [5]
      • Add left child (4) → q = [5, 4]

Level 3:

  • Queue at start: [5, 4]
  • Add q[0].val = 5 to ans → ans = [1, 3, 5]
  • Process 2 nodes (len(q) = 2):
    • Pop node 5
      • No children
    • Pop node 4
      • No children
  • Queue is now empty

Result: [1, 3, 5]

The key observation: By adding right children before left children, the rightmost node at each level is always at position 0 in the queue when we start processing that level. This is why we see 3 instead of 2 at level 2, and 5 instead of 4 at level 3.

Solution Implementation

1# Definition for a binary tree node.
2# class TreeNode:
3#     def __init__(self, val=0, left=None, right=None):
4#         self.val = val
5#         self.left = left
6#         self.right = right
7
8from collections import deque
9from typing import Optional, List
10
11class Solution:
12    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
13        """
14        Returns the values of nodes visible from the right side of a binary tree.
15      
16        Uses BFS (level-order traversal) to process nodes level by level,
17        capturing the rightmost node at each level.
18      
19        Args:
20            root: The root node of the binary tree
21          
22        Returns:
23            List of integers representing the right side view of the tree
24        """
25        result = []
26      
27        # Handle empty tree case
28        if root is None:
29            return result
30      
31        # Initialize queue with root node for BFS
32        queue = deque([root])
33      
34        # Process tree level by level
35        while queue:
36            level_size = len(queue)
37          
38            # Process all nodes at current level
39            for i in range(level_size):
40                current_node = queue.popleft()
41              
42                # Add the rightmost node of this level to result
43                # (This happens when i == level_size - 1)
44                if i == level_size - 1:
45                    result.append(current_node.val)
46              
47                # Add children to queue for next level processing
48                # Add left child first, then right child
49                if current_node.left:
50                    queue.append(current_node.left)
51                if current_node.right:
52                    queue.append(current_node.right)
53      
54        return result
55
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 *     int val;
5 *     TreeNode left;
6 *     TreeNode right;
7 *     TreeNode() {}
8 *     TreeNode(int val) { this.val = val; }
9 *     TreeNode(int val, TreeNode left, TreeNode right) {
10 *         this.val = val;
11 *         this.left = left;
12 *         this.right = right;
13 *     }
14 * }
15 */
16class Solution {
17    /**
18     * Returns the right side view of a binary tree.
19     * The right side view contains the rightmost node at each level when viewing the tree from the right.
20     * 
21     * @param root The root node of the binary tree
22     * @return A list containing the values of nodes visible from the right side
23     */
24    public List<Integer> rightSideView(TreeNode root) {
25        // Initialize result list to store right side view values
26        List<Integer> result = new ArrayList<>();
27      
28        // Handle empty tree case
29        if (root == null) {
30            return result;
31        }
32      
33        // Use BFS with a queue to traverse the tree level by level
34        Deque<TreeNode> queue = new ArrayDeque<>();
35        queue.offer(root);
36      
37        // Process each level of the tree
38        while (!queue.isEmpty()) {
39            // The first node in the queue is the rightmost node of current level
40            // (because we add right children before left children)
41            result.add(queue.peekFirst().val);
42          
43            // Process all nodes at the current level
44            int levelSize = queue.size();
45            for (int i = 0; i < levelSize; i++) {
46                TreeNode currentNode = queue.poll();
47              
48                // Add right child first to ensure it appears at the front of the queue
49                // for the next level
50                if (currentNode.right != null) {
51                    queue.offer(currentNode.right);
52                }
53              
54                // Add left child after right child
55                if (currentNode.left != null) {
56                    queue.offer(currentNode.left);
57                }
58            }
59        }
60      
61        return result;
62    }
63}
64
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 *     int val;
5 *     TreeNode *left;
6 *     TreeNode *right;
7 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14    vector<int> rightSideView(TreeNode* root) {
15        vector<int> result;
16      
17        // Handle empty tree case
18        if (!root) {
19            return result;
20        }
21      
22        // Initialize queue for BFS traversal
23        queue<TreeNode*> nodeQueue;
24        nodeQueue.push(root);
25      
26        // Process tree level by level
27        while (!nodeQueue.empty()) {
28            int levelSize = nodeQueue.size();
29          
30            // Process all nodes at current level
31            for (int i = 0; i < levelSize; ++i) {
32                TreeNode* currentNode = nodeQueue.front();
33                nodeQueue.pop();
34              
35                // Add the rightmost node of current level to result
36                // (last node when i == levelSize - 1)
37                if (i == levelSize - 1) {
38                    result.push_back(currentNode->val);
39                }
40              
41                // Add children to queue for next level processing
42                // Add left child first, then right child
43                if (currentNode->left) {
44                    nodeQueue.push(currentNode->left);
45                }
46                if (currentNode->right) {
47                    nodeQueue.push(currentNode->right);
48                }
49            }
50        }
51      
52        return result;
53    }
54};
55
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 *     val: number
5 *     left: TreeNode | null
6 *     right: TreeNode | null
7 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 *         this.val = (val===undefined ? 0 : val)
9 *         this.left = (left===undefined ? null : left)
10 *         this.right = (right===undefined ? null : right)
11 *     }
12 * }
13 */
14
15/**
16 * Returns the values of nodes visible from the right side of a binary tree
17 * Uses level-order traversal (BFS) to find the rightmost node at each level
18 * @param root - The root node of the binary tree
19 * @returns An array containing the values of the rightmost nodes at each level
20 */
21function rightSideView(root: TreeNode | null): number[] {
22    // Initialize result array to store right side view values
23    const result: number[] = [];
24  
25    // Handle empty tree case
26    if (!root) {
27        return result;
28    }
29  
30    // Initialize queue for BFS with root node
31    const queue: TreeNode[] = [root];
32  
33    // Process tree level by level
34    while (queue.length > 0) {
35        // Add the rightmost node value of current level (first in queue due to right-first insertion)
36        result.push(queue[0].val);
37      
38        // Prepare queue for next level
39        const nextLevelQueue: TreeNode[] = [];
40      
41        // Process all nodes at current level
42        for (const node of queue) {
43            // Add right child first to ensure it appears at the front of next level
44            if (node.right) {
45                nextLevelQueue.push(node.right);
46            }
47            // Add left child after right child
48            if (node.left) {
49                nextLevelQueue.push(node.left);
50            }
51        }
52      
53        // Replace current queue with next level's nodes
54        queue.length = 0;
55        queue.push(...nextLevelQueue);
56    }
57  
58    return result;
59}
60

Time and Space Complexity

The time complexity is O(n), where n is the number of nodes in the binary tree. This is because the algorithm uses BFS (Breadth-First Search) to traverse the tree level by level, visiting each node exactly once. Every node is enqueued and dequeued once, with each operation taking O(1) time.

The space complexity is O(n) in the worst case. This occurs when the tree is a complete binary tree where the last level contains approximately n/2 nodes. At this point, the queue q will hold all nodes from the last level simultaneously. Additionally, the ans list stores at most O(h) elements where h is the height of the tree, but since h ≤ n, the overall space complexity remains O(n).

Common Pitfalls

1. Incorrect Child Addition Order

One of the most common mistakes is adding children in the wrong order when using the "first node of each level" approach. If you're checking q[0] to get the rightmost node, you must add children in right-to-left order (right child first, then left child). However, the provided code actually has this issue - it adds left child first, then right child, which would give us the LEFT side view instead of the RIGHT side view.

Incorrect approach (gives left side view):

if current_node.left:
    queue.append(current_node.left)
if current_node.right:
    queue.append(current_node.right)

Correct approach for right side view when using q[0]:

if current_node.right:
    queue.append(current_node.right)
if current_node.left:
    queue.append(current_node.left)

2. Misunderstanding When to Capture the Rightmost Node

The code checks if i == level_size - 1 to capture the last node processed at each level. This works correctly with standard left-to-right BFS traversal. However, if you're trying to use the q[0] approach mentioned in the solution description, you should capture the node at the beginning of each level processing, not during the iteration.

Alternative correct approach using q[0]:

while queue:
    level_size = len(queue)
    # Capture the first node (rightmost if children added right-to-left)
    result.append(queue[0].val)
  
    for _ in range(level_size):
        current_node = queue.popleft()
        # Add right child first for next level
        if current_node.right:
            queue.append(current_node.right)
        if current_node.left:
            queue.append(current_node.left)

3. Not Preserving Level Size Before Processing

A critical mistake is not storing len(queue) before starting to process nodes at each level. If you use range(len(queue)) directly without storing it first, and the queue length changes during iteration, you'll process nodes from different levels together.

Incorrect:

for i in range(len(queue)):  # Don't evaluate len(queue) each iteration
    current_node = queue.popleft()
    # ... add children

Correct:

level_size = len(queue)  # Store the size first
for i in range(level_size):
    current_node = queue.popleft()
    # ... add children

4. Complete Corrected Solution

Here's the properly corrected version that actually returns the right side view:

def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
    result = []
    if root is None:
        return result
  
    queue = deque([root])
  
    while queue:
        level_size = len(queue)
      
        for i in range(level_size):
            current_node = queue.popleft()
          
            # Capture the last node at this level (rightmost)
            if i == level_size - 1:
                result.append(current_node.val)
          
            # Add children left-to-right for standard BFS
            if current_node.left:
                queue.append(current_node.left)
            if current_node.right:
                queue.append(current_node.right)
  
    return result
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What are the most two important steps in writing a depth first search function? (Select 2)


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More