Skip to main content

Binary Tree Right Side View Algorithm Solution

Complete Java implementation for binary tree right side view problem with BFS and DFS approaches, including time/space complexity analysis.

The binary tree right side view problem requires returning the values of nodes visible when looking at a binary tree from the right side. This classic tree traversal problem can be solved efficiently using either BFS or DFS approaches with different trade-offs in implementation complexity and performance characteristics.

1. Problem Restatement

Clarifying questions to ask the interviewer:

  1. Can we mutate the input tree nodes, or do we need to preserve the original structure?
  2. What is the expected time complexity target - O(n) or better?
  3. Are we guaranteed that the tree will have at least one node, or should we handle empty input?
  4. How large can the tree be in terms of nodes (e.g., up to 10^4 nodes)?
  5. Should we return the values in left-to-right order or right-to-left order?

Restatement of the problem: Given a binary tree, return the values of the nodes visible from the right side when looking at the tree from top to bottom, ordered from top to bottom.

Clarification of inputs and outputs:

  • Input: A binary tree represented by its root node (TreeNode)
  • Output: A list of integers representing the rightmost node values at each level
  • Each level's rightmost node is visible, and we collect these values in order from top to bottom

Assumptions:

  • The tree can have 0 or more nodes
  • We're only interested in the rightmost visible node per level
  • The solution should return values in top-to-bottom order (level 0, then level 1, etc.)

Data shape and hidden structure:

  • Binary tree with left and right child pointers
  • The problem has a natural level-by-level structure that suggests BFS or DFS traversal with level tracking
  • Each node's visibility depends on its position relative to other nodes at the same level

The core challenge is efficiently identifying which node from each level is visible from the right side, requiring either breadth-first traversal to process levels or depth-first traversal with level tracking.

2. Pattern Recognition

Identifying the most likely pattern or patterns

The most likely pattern here is Level-order traversal (BFS) with result collection by level. This is a classic binary tree traversal problem where we need to capture specific elements from each level of the tree.

Wording that signals the pattern

  • "Right side view" - This signals we want to capture elements from the rightmost position at each level
  • "View from the right side" - Indicates we're looking at the tree level by level from a particular perspective
  • "Each level" - This suggests we need to process nodes in groups by their depth/level

Why this problem fits that pattern

This problem is fundamentally about tree level grouping. When we view a binary tree from the right side, we see exactly one node per level - the rightmost node at that depth. This naturally maps to a level-order traversal where we:

  1. Process nodes level by level
  2. Capture the rightmost node at each level
  3. Maintain the order of processing from left to right within each level

Core insight needed to solve it

The key insight is that we need to track which level each node belongs to and only keep the rightmost node seen at each level. This can be achieved by:

  • Using a queue for BFS traversal
  • Tracking the current level of each node
  • For each level, we only store the first node we encounter from the right side (when processing left to right)
  • Or more efficiently, we can process each level from right to left and capture the first node we encounter

This pattern is commonly solved using BFS with level tracking or DFS with depth tracking, where we collect the first node encountered at each depth level from the right side.

3. Approach Evolution

My first instinct is to think about how we can capture the rightmost node at each level of the binary tree. This naturally suggests a level-order traversal (BFS) approach where we process nodes level by level and record the last node we encounter at each level.

However, that would be O(n²) in the worst case if we're checking every node at each level to find the rightmost one, because for each level we'd need to traverse all nodes at that level. Let me think about how to optimize this.

I notice that if I maintain a queue that only stores nodes at the current level, and I process them one by one from left to right, then the last node processed at each level will be the rightmost node. But that's still O(n) space in worst case for the queue.

Wait, I can do better. If I use a recursive DFS approach and track the depth, I can ensure that for each level, I only keep the first node I encounter when visiting from right to left. Actually, let me reconsider: if I traverse the tree in a way that I visit right children before left children at each level, then for each level I only need to record the first node I encounter.

That's a key insight! If I do a DFS traversal and visit right child before left child, then for each level I'll encounter the rightmost node first. So I can maintain a result list where I only add nodes to levels that haven't been added yet.

This approach reduces the problem to O(n) time and O(h) space where h is the height of the tree, because we only visit each node once and the recursion stack depth is at most h.

Let me think about this more carefully. In a standard DFS, I'd visit root, then left subtree, then right subtree. But if I want to capture the rightmost view, I should visit root, then right subtree, then left subtree. This way, for each level, the first node I encounter will be the rightmost one.

The key optimization is that instead of doing a full BFS and collecting all nodes per level, I can do a DFS with right-first traversal and only keep the first node encountered at each depth level.

This approach is clean, efficient, and avoids storing entire levels of nodes. It's also more intuitive once you understand that we're essentially collecting the first node encountered at each level when traversing right-to-left.

The invariant I must maintain is that at any point in the traversal, the result list contains only the rightmost node for each level that has been visited so far. When I reach a new level, I add the current node to the result list if it's the first one at that level.

4. Data Structure Choice

For the binary tree right side view problem, I need to carefully select data structures that will support efficient traversal and result collection.

Primary Data Structures:

  1. Queue (LinkedList) - Used for level-order traversal (BFS). This is essential because:

    • It naturally supports the FIFO pattern needed for processing nodes level by level
    • Allows me to process all nodes at the current level before moving to the next level
    • Provides O(1) add/remove operations for efficient traversal
  2. List (ArrayList) - Used to store the final result:

    • Stores the rightmost node value at each level
    • Provides O(1) random access to retrieve values by level index
    • Allows for easy expansion as we discover new levels

Why These Choices Are Optimal:

The queue is the natural choice for BFS traversal since it maintains the order of nodes at each level. For each level, I process all nodes in that level before moving to the next, which is exactly what a queue provides.

The ArrayList is ideal for storing results because:

  • I know the maximum number of levels equals the tree height, so I can efficiently index into results
  • The rightmost node at each level is simply the last node processed in that level's queue iteration
  • It avoids the complexity of maintaining a map or other more complex structures

Helper Structures:

I don't need any additional helper data structures beyond the queue and result list. The queue naturally maintains the level information through its structure, and I can determine when I've completed processing a level by checking the queue size.

The combination of these two structures gives me clean, readable code with optimal performance characteristics for this specific traversal pattern.

5. Algorithm Walkthrough

Let me trace through the algorithm with a concrete example to show how it works step by step.

Consider this binary tree:

    1
   / \
  2   3
 / \   \
4   5   6

Step 1: Initialize queue with root node (1) and result list as empty.

  • Queue: [(1, 1)]
  • Result: []

Step 2: Process level 1 (root).

  • Dequeue (1, 1) - node 1 at depth 1
  • Since result size (0) < current depth (1), add node 1 to result
  • Add children of node 1 to queue: (2, 2), (3, 2)
  • Queue: [(2, 2), (3, 2)]
  • Result: [1]

Step 3: Process level 2.

  • Dequeue (2, 2) - node 2 at depth 2
  • Since result size (1) < current depth (2), add node 2 to result
  • Add children of node 2 to queue: (4, 3), (5, 3)
  • Queue: [(3, 2), (4, 3), (5, 3)]
  • Result: [1, 2]

Step 4: Process remaining nodes at level 2.

  • Dequeue (3, 2) - node 3 at depth 2
  • Since result size (1) = current depth (2), we skip this node as it's not the rightmost
  • Add children of node 3 to queue: (6, 3)
  • Queue: [(4, 3), (5, 3), (6, 3)]

Step 5: Process level 3.

  • Dequeue (4, 3) - node 4 at depth 3
  • Since result size (2) < current depth (3), add node 4 to result
  • Add children of node 4 to queue: (null, 4), (null, 4)
  • Queue: [(5, 3), (6, 3)]
  • Result: [1, 2, 4]

Step 6: Process remaining nodes at level 3.

  • Dequeue (5, 3) - node 5 at depth 3
  • Since result size (2) = current depth (3), we skip this node as it's not the rightmost
  • Dequeue (6, 3) - node 6 at depth 3
  • Since result size (2) = current depth (3), we skip this node as it's not the rightmost

Final Result: [1, 2, 4]

The key insight is that for each level, we only add the first node we encounter at that level to our result list. This ensures we capture the rightmost visible node at each level, which is exactly what we want for the right side view.

The algorithm correctly handles the case where a level has multiple nodes by only including the first one processed at that depth, which will always be the rightmost due to our BFS traversal order (left to right).

6. Java Solution

import java.util.*;

// Definition for a binary tree node.
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

public class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        
        // Handle empty tree
        if (root == null) {
            return result;
        }
        
        // Use BFS with queue to process level by level
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        
        while (!queue.isEmpty()) {
            int levelSize = queue.size();
            
            // Process all nodes at current level
            for (int i = 0; i < levelSize; i++) {
                TreeNode node = queue.poll();
                
                // Add the rightmost node of this level to result
                if (i == levelSize - 1) {
                    result.add(node.val);
                }
                
                // Add children to queue for next level processing
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
        }
        
        return result;
    }
}

This solution uses level-order traversal (BFS) to process the binary tree level by level. For each level, we only add the rightmost node's value to our result list. The key insight is that in a level-order traversal, we can identify the rightmost node of each level by checking if we're processing the last element of that level's size. This approach ensures we capture exactly one node per level from the right side, giving us the desired right-side view.

7. Complexity Analysis

Time Complexity: O(n) where n is the number of nodes in the binary tree.

The algorithm performs a single level-order traversal (BFS) of the tree, visiting each node exactly once. For each node, we perform O(1) operations: checking if it's the rightmost node at its level, adding to result list, and enqueueing children. Since we process each of the n nodes exactly once, the time complexity is linear.

Space Complexity: O(w) where w is the maximum width of the binary tree.

The space usage comes from two sources:

  1. The queue used for BFS traversal, which at any point contains at most the maximum number of nodes at any level
  2. The result list, which contains at most one node per level (h levels), so O(h) space

In the worst case, for a complete binary tree with maximum width w = 2^(h-1), the space complexity approaches O(2^h) where h is the height. However, since we're typically interested in the width of the tree rather than its height, we express it as O(w) where w is the maximum width.

The solution is optimal because:

  • We must examine each node at least once to determine if it's visible from the right side
  • The algorithm uses the minimum necessary data structures (queue for traversal, list for result)
  • No additional preprocessing or sorting is required
  • The space usage matches the information content (at most one node per level needs to be stored)

This is near-optimal for the problem since we cannot avoid visiting all nodes in the worst case, and our space usage is proportional to the information content rather than the total input size.

8. Edge Cases & Pitfalls

Empty and Minimal Inputs

  • Empty tree: When root == null, the result should be an empty list []
  • Single node: When tree has only root node, result should contain that single value
  • Only left children: Tree shaped like a linked list with only left children should return the first element of each level
  • Only right children: Tree shaped like a linked list with only right children should return all nodes in order

Duplicate Values and Repeated States

  • Duplicate node values: The algorithm should not be affected by duplicate values in the tree - we only care about the rightmost node per level
  • Same value at different levels: Same value can appear at multiple levels and should be handled correctly

Boundary Conditions

  • Level 0 (root): Always included in result as it's the rightmost node of level 0
  • Level 1: If root has only left child, rightmost node is null; if root has only right child, it's the right child
  • Maximum depth: Need to handle trees with maximum possible depth (up to 100 in constraints)

Common Implementation Mistakes

  • Incorrect level tracking: Using a global variable instead of passing current level correctly to recursive calls
  • Wrong collection order: Adding nodes in left-to-right order instead of right-to-left, which can cause wrong node selection
  • Not handling null children properly: Missing null checks when traversing left/right children
  • Using wrong data structure: Using Stack instead of Queue for BFS, or not maintaining proper level ordering
  • Off-by-one errors: Miscounting levels when using 0-based vs 1-based indexing
  • Not preserving insertion order: In BFS approach, not processing nodes in left-to-right order to ensure rightmost is selected last

Implementation-specific Pitfalls

  • Modifying input: Not realizing that we should not mutate the input tree structure
  • Incorrect queue handling: In BFS, not properly tracking which nodes belong to which level when using a single queue
  • Missing base case handling: Not properly handling the case where root is null before entering any traversal logic

9. Alternative Approaches

There are several alternative approaches to solving the binary tree right side view problem, each with different trade-offs:

BFS with Level Tracking: Instead of using a queue, we can use a breadth-first search approach where we process nodes level by level. For each level, we keep track of the rightmost node encountered and add it to our result. This approach requires maintaining a queue that stores nodes along with their levels, and we process nodes in order from left to right within each level. The time complexity remains O(n) but with higher constant factors due to the additional level tracking overhead.

Two-Queue BFS: We can maintain two separate queues - one for the current level and one for the next level. After processing all nodes in the current level, we know that the last node processed is the rightmost one for that level. This approach avoids level tracking but requires managing two queues and can be slightly more complex to implement correctly.

DFS with Depth Tracking: A depth-first search approach where we traverse the tree and maintain a map or array indexed by depth to store the rightmost node value at each level. We process nodes in right-to-left order (right child first, then left child) to ensure that the first node we encounter at each depth is the rightmost one. This approach has O(n) time complexity but uses O(d) extra space where d is the depth of the tree.

Stack-Based DFS: We can simulate DFS using an explicit stack, storing both nodes and their depths. By processing right children before left children, we can ensure that when we first visit a node at a given depth, it's the rightmost one. This approach is similar to the map-based DFS but uses a stack instead of a hash map.

The primary advantage of the chosen solution (DFS with depth tracking) is its clean implementation and optimal space usage. The alternative approaches offer similar time complexity but may introduce additional overhead or complexity in implementation, particularly when dealing with edge cases like empty trees or unbalanced trees. The stack-based approach might be preferred in environments where recursion depth is a concern, but it doesn't provide significant advantages over the chosen solution in terms of performance or clarity.

10. Test Cases

// Test case 1: Basic binary tree
//     1
//    / \
//   2   3
//  / \   \
// 4   5   6
// Right side view: [1,3,6]
TreeNode root1 = new TreeNode(1);
root1.left = new TreeNode(2);
root1.right = new TreeNode(3);
root1.left.left = new TreeNode(4);
root1.left.right = new TreeNode(5);
root1.right.right = new TreeNode(6);

// Test case 2: Single node
//   1
// Right side view: [1]
TreeNode root2 = new TreeNode(1);

// Test case 3: Empty tree
// Right side view: []
TreeNode root3 = null;

// Test case 4: Skewed tree (left)
//   1
//    \
//     2
//      \
//       3
// Right side view: [1,2,3]
TreeNode root4 = new TreeNode(1);
root4.right = new TreeNode(2);
root4.right.right = new TreeNode(3);

// Test case 5: Complete binary tree
//       1
//      / \
//     2   3
//    / \ / \
//   4  5 6  7
// Right side view: [1,3,7]
TreeNode root5 = new TreeNode(1);
root5.left = new TreeNode(2);
root5.right = new TreeNode(3);
root5.left.left = new TreeNode(4);
root5.left.right = new TreeNode(5);
root5.right.left = new TreeNode(6);
root5.right.right = new TreeNode(7);

The test cases cover:

  • A general binary tree with multiple levels
  • Edge case of single node
  • Empty input scenario
  • Skewed tree (right-leaning) to test boundary conditions
  • Complete binary tree to ensure proper level-order traversal

Each case validates that the rightmost node at each level is correctly identified, with proper handling of null children and varying tree structures.

11. Final Summary

The core idea is to capture the rightmost node at each level of a binary tree by performing a depth-first traversal while tracking the maximum depth seen so far. This solution works efficiently by prioritizing the right subtree in recursive calls, ensuring that the first node visited at each new depth level is the rightmost one. The approach is optimal with O(n) time complexity and O(h) space complexity where h is the tree height, making it both time and space efficient for the given problem constraints.

Was this article helpful?

Topics in this article

More from DSA Problems

  • DSA Problems

    Maximum Depth of Binary Tree Solution

    Complete Java implementation for finding maximum depth of binary tree with BFS and DFS approaches, including time/space complexity analysis.

  • DSA Patterns

    Level Order Traversal of Binary Trees

    Complete Java solution for level order traversal with BFS approach, including edge case handling and time complexity analysis.

  • DSA Problems

    Lowest Common Ancestor of a Binary Tree

    Find the lowest common ancestor of two nodes in a binary tree with an efficient recursive approach.

  • DSA Problems

    Diameter of Binary Tree Solution in Java

    Complete Java implementation for finding the diameter of a binary tree with optimal time and space complexity analysis.

Stay in the Loop

Get curated DSA patterns, coding problems, frontend and backend LLD/HLD, and AI/ML System Design insights delivered straight to your inbox. No spam, just focused interview and engineering content.