Start learning free

LESSON 445 OF 573 · BINARY TREES

Binary Tree Right Side View in Scala: Level-Order BFS

Practice O(n) Read the full explanation

Given the root of a binary tree, imagine yourself standing on its right side. Return the values of the nodes you can see, ordered from top to bottom.

From the right you see exactly one node on each level: the rightmost node of that level, the last one when you read the level from left to right. It can belong to the left subtree, when the right subtree doesn't reach that deep. An empty tree shows nothing.

The trees below are written in level order (top to bottom, left to right), with null for a missing child.

 

Example 1:

    1
   / \
  2   3
   \   \
    5   4

Input: root = [1,2,3,null,5,null,4]
Output: [1,3,4]
Explanation: The first level is just 1. On the second level, 3 hides 2. On the third level, 4 hides 5.

Example 2:

      1
     / \
    2   3
   /
  4

Input: root = [1,2,3,4]
Output: [1,3,4]
Explanation: The third level has only 4. It is in the left subtree, but nothing stands to its right, so you see it.

Example 3:

Input: root = []
Output: []
Explanation: An empty tree has no levels, so you see no nodes.

 

Constraints:

  • The number of nodes in the tree is in the range [0, 100].
  • -100 <= Node.val <= 100
  • Values may repeat.

Note:

The tutorial walks the tree level by level with a queue. The queue starts with the root. At the start of each round it holds exactly one level: count its nodes, take them out one by one from the front, and put each node's children (left, then right) at the back, where they wait for the next round. The last node taken out in a round is the rightmost node of its level: that is the one you see.

Each node enters and leaves the queue once, so the time is O(n) for a tree of n nodes. The queue holds at most about two levels at a time, which is O(n) extra space in the worst case.

STEP 1 OF 11
    Ctrl + Enter to check

    SCALA · BINARY TREES

    Binary Tree Right Side View in Scala, explained

    Problem overview

    The method rightSideView receives the root of a binary tree and returns a List[Int]: the values you would see standing on the tree's right side, from the top level to the bottom one. Exactly one node of each level is visible, the rightmost one. An empty tree (root == null) gives an empty list.

    The lesson provides the TreeNode class (it is shown in a comment above the starter code): class TreeNode(var value: Int), with var left: TreeNode = null and var right: TreeNode = null. A missing child is null.

    For the tree [1,2,3,null,5,null,4], level 1 is just 1; level 2 is 2, 3, and 3 hides 2; level 3 is 5, 4, and 4 hides 5. The method returns List(1, 3, 4).

    Approach: walk the tree one level at a time

    A tempting shortcut is to follow right pointers from the root. It works for the first example, but it fails as soon as a level is reached only through the left subtree. For the tree [1,2,3,4], node 4 is the left child of 2, and nothing stands to its right, so it is visible. Following right pointers gives List(1, 3), while the correct answer is List(1, 3, 4).

    The reliable idea is to visit the tree level by level, which is a breadth-first traversal. A queue does it: nodes leave from the front, and their children join at the back. If the queue holds exactly one level at the start of a round, then the last node taken out in that round is the rightmost node of the level, which is the one to record. The children added during the round wait behind the rest of the level, so they form the next round.

    A depth-first search can also do the job by tracking the depth, but the queue version matches the way the question is phrased (one value per level) and needs no recursion.

    Step by step

    1. Create result, an empty ArrayBuffer[Int](). It collects one value per level.
    2. Handle the empty tree. If root == null, return result.toList immediately. The queue needs a first node to start from.
    3. Create the queue as a mutable.Queue[TreeNode] holding root. enqueue adds at the back, dequeue() removes from the front.
    4. Loop while queue.nonEmpty. Each pass of this loop handles one whole level.
    5. Record the level's size, queue.size, in levelSize. Right now the queue contains only this level, so the number is the count of its nodes. It must be stored before the inner loop, because the queue keeps changing while nodes are added.
    6. Loop i over 0 until levelSize. This takes exactly the nodes of the current level and stops before the children added during the loop.
    7. Dequeue a node. Nodes leave in left-to-right order.
    8. If i == levelSize - 1, the node is the last of its level: add node.value to result with +=.
    9. Enqueue the left child when it is not null.
    10. Enqueue the right child when it is not null, right after the left one, so every level stays in left-to-right order.
    11. Finish with result.toList, the last expression of the method.

    The solution in Scala

    class Solution {
        def rightSideView(root: TreeNode): List[Int] = {
            val result = ArrayBuffer[Int]()
            if (root == null) {
                return result.toList
            }
            val queue = mutable.Queue[TreeNode](root)
            while (queue.nonEmpty) {
                val levelSize = queue.size
                for (i <- 0 until levelSize) {
                    val node = queue.dequeue()
                    if (i == levelSize - 1) {
                        result += node.value
                    }
                    if (node.left != null) {
                        queue.enqueue(node.left)
                    }
                    if (node.right != null) {
                        queue.enqueue(node.right)
                    }
                }
            }
            result.toList
        }
    }
    

    Scala details worth a look:

    • 0 until levelSize excludes levelSize, so i runs from 0 to levelSize - 1, one turn per node of the level. 0 to levelSize would include levelSize and take one turn too many.
    • queue.size is read once. The inner loop enqueues children, so reading queue.size again would count nodes of the next level too. The val levelSize keeps the number from before.
    • null checks. The tree uses null for missing children, so the code compares with != null before enqueueing. A null in the queue would make the code fail with a NullPointerException as soon as it reads a field of it.
    • return inside the method. The early return result.toList is fine here, because it is not inside a lambda. The final line result.toList is the method's value without return.
    • ArrayBuffer versus List. ArrayBuffer grows cheaply at its end with +=. The method promises an immutable List[Int], so toList converts it at the end.

    Complexity

    Time: O(n) for a tree of n nodes. Each node enters the queue once and leaves it once, with a constant amount of work.

    Space: O(n) in the worst case. The queue holds at most about two levels at a time, and a level of a complete binary tree can contain around half of all the nodes. The result holds one value per level.

    Edge cases and common mistakes

    • Empty tree. The early return gives List(). Without it, the queue would start with a null, and the loop would throw a NullPointerException.
    • A single node. [7] gives List(7): the level has one node and it is also the last one.
    • A level seen only through the left subtree. For [1,2,null,3,null,4], every node is the only one on its level, and the answer is List(1, 2, 3, 4). Following only right pointers returns List(1).
    • Reading queue.size again inside the inner loop, in the test i == queue.size - 1. The size changes as nodes leave and children join, so the test picks the wrong nodes: on [1,2,3,null,5,null,4] that version returns List(2, 5).
    • Recording the first node of each level instead of the last. That gives the left side view: List(1, 2, 5) for the same tree.
    • Repeated values. Values may repeat; the code works with nodes, not with values, so duplicates change nothing.

    Testing

    Trees are written in level order, with null for a missing child.

    • [1,2,3,null,5,null,4] gives List(1, 3, 4).
    • [1,2,3,4] gives List(1, 3, 4).
    • [] gives List().
    • [1,null,3] gives List(1, 3).
    • [1,2,null,3,null,4] gives List(1, 2, 3, 4).
    • [7] gives List(7).
    Keep going with Scala500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free