Start learning free

LESSON 445 OF 573 · BINARY TREES

Binary Tree Right Side View in TypeScript: Level Order with a Queue

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

    TYPESCRIPT · BINARY TREES

    Binary Tree Right Side View in TypeScript, explained

    Problem overview

    The method rightSideView receives the root of a binary tree, or null for an empty tree. It returns the values of the nodes you would see standing on the right side of the tree, from the top level to the bottom. The lesson provides the TreeNode class, with the fields val, left and right.

    From the right you see one node per level: the rightmost one, which is the last node when the level is read from left to right. For the tree written in level order as [1, 2, 3, null, 5, null, 4], the first level is 1. On the second level 3 hides 2, and on the third level 4 hides 5, so the answer is [1, 3, 4]. For [1, 2, 3, 4] the answer is also [1, 3, 4]: the third level holds only 4, which sits in the left subtree, but nothing is to its right, so it is visible. An empty tree gives [].

    Approach: walk the tree one level at a time

    A tempting shortcut is to follow only right children from the root. On [1, 2, 3, 4] that visits 1 and then 3, and returns [1, 3], missing the 4. The rightmost node of a level can belong to the left subtree whenever the right subtree is shallower, so the solution has to look at whole levels.

    Visiting a tree level by level is the job of a queue: nodes leave from the front, and their children enter at the back. If the queue holds exactly one level at the start of a round, then taking out that many nodes empties the level while the children of those nodes pile up behind, forming the next level. The last node taken out in a round is the rightmost node of its level, and that is the value to record.

    Each node enters and leaves the queue once, so the time is O(n) for a tree of n nodes.

    A queue without shift()

    JavaScript arrays have shift(), which removes the first element, but it moves every other element one place to the left, so it can be slow on long arrays. The lesson uses a cheaper trick: keep the array as it is and a counter head that marks the next node to take. The nodes from head to the end of the array are the ones still waiting. Reading queue[head] and then incrementing head costs O(1). The visited nodes stay in the array, which is fine here because they are only a few references.

    Step by step

    1. Create result as an empty number[]. It will collect one value per level.
    2. Handle the empty tree. If !root, return result right away. The queue needs a first node to start from, and an empty tree shows nothing.
    3. Create the queue as a TreeNode[] that holds root, and set head to 0.
    4. Loop while head < queue.length. As long as some node is waiting, there is another level to handle. Each pass of this loop handles exactly one level.
    5. Store the level's size as queue.length - head in a constant named levelSize. At this point the waiting nodes are one whole level and nothing else.
    6. Loop i from 0 to levelSize - 1. The constant matters: the loop adds children to queue while it runs, so reading queue.length - head in the loop condition would keep growing and run into the next level. Counting to a number fixed in advance stops at the end of this level.
    7. Take the next node: read queue[head] into node and advance head.
    8. Record the last node of the level: when i === levelSize - 1, push node.val onto result.
    9. Enqueue the left child if it exists. It waits behind the rest of this level.
    10. Enqueue the right child if it exists, right after the left one, so every level stays in left-to-right order.
    11. Return result after the loops.

    The solution in TypeScript

    class Solution {
        rightSideView(root: TreeNode | null): number[] {
            const result: number[] = [];
            if (!root) {
                return result;
            }
            const queue: TreeNode[] = [root];
            let head = 0;
            while (head < queue.length) {
                const levelSize = queue.length - head;
                for (let i = 0; i < levelSize; i++) {
                    const node = queue[head];
                    head++;
                    if (i === levelSize - 1) {
                        result.push(node.val);
                    }
                    if (node.left) {
                        queue.push(node.left);
                    }
                    if (node.right) {
                        queue.push(node.right);
                    }
                }
            }
            return result;
        }
    }
    

    A few TypeScript details:

    • TreeNode | null. The parameter can be a node or null. The if (!root) check returns early for null, so the code after it can use root as a node.
    • Truthiness checks. if (node.left) is true for any node object and false for null, so it tests for a missing child before pushing it.
    • Annotations are erased. number[] and TreeNode[] help the compiler and disappear in the generated JavaScript, so they cost nothing at run time.
    • const inside loops. levelSize and node are created again on every pass, which is what you want, while head is declared with let because it changes.

    Complexity

    Time: O(n). Every node is added to the queue once and read once, and each of those steps takes constant time.

    Space: O(n). The array keeps every node that has been visited, since nothing is removed. Even if you removed them, the queue holds up to a whole level at a time, and in a complete tree the last level has about half of the nodes. The result array is small by comparison: one value per level.

    Edge cases and common mistakes

    • Empty tree. Without the early return, [root] would hold null, and reading node.val would throw. With it, [] gives [].
    • Single node. A tree with only the root 7 gives [7].
    • Following only right children. It gives [1, 3] for [1, 2, 3, 4] instead of [1, 3, 4], as explained above.
    • Only a left spine. The tree [1, 2, null, 3, null, 4] has no right children at all, yet the answer is [1, 2, 3, 4], because each level's only node is visible.
    • Enqueueing the right child first. The last node of a level would then be the leftmost one, and you would return the left side view instead.
    • Recording the first node of a level. The same left-side mistake. The check must be on the last index, levelSize - 1.
    • Values may repeat. The result holds values, not nodes, and the algorithm never compares values, so duplicates change nothing.

    Testing

    These inputs are level-order lists, with null for a missing child, and the outputs are what the solution returns.

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