Start learning free

LESSON 446 OF 574 · BINARY TREES

Binary Tree Right Side View in Swift: 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 nil for a missing child.

 

Example 1:

    1
   / \
  2   3
   \   \
    5   4

Input: root = [1,2,3,nil,5,nil,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

    SWIFT · BINARY TREES

    Binary Tree Right Side View in Swift, explained

    Problem overview

    The method rightSideView receives the root of a binary tree, as an optional TreeNode?. Imagine standing on the right of the tree and looking at it: on every level you see exactly one node, the rightmost one. The method returns those values from the top level to the bottom one. An empty tree (root is nil) shows nothing, so the answer is [].

    The lesson provides the TreeNode class: each node has a val of type Int and two optional children, left and right. In the example [1,2,3,nil,5,nil,4], node 1 is the root, its children are 2 and 3, node 2 has only a right child (5) and node 3 has only a right child (4). The levels are [1], [2, 3] and [5, 4], and the last value of each level is what you see: [1, 3, 4].

    Approach: visit the tree one level at a time

    The tempting shortcut is to start at the root and keep going to the right child. It works when every level has a right node, and it fails otherwise. For the tree [1,2,3,4], the third level holds only node 4, which hangs under node 2 on the left. Following right children gives [1, 3], but the correct answer is [1, 3, 4], because nothing stands to the right of 4 and you see it. In the same way, the tree [1,2,nil,3,nil,4], which has no right children at all, must give [1, 2, 3, 4], while the shortcut returns [1].

    So the question has to be asked per level: which node is the last one when the level is read from left to right? That is what a level order traversal (breadth-first search) gives. A queue starts with the root. In each round the queue holds exactly one level. The round takes those nodes out from the front, and each node adds its children, left first and then right, at the back. Those children wait for the next round, so the nodes of a level always leave the queue from left to right. The last node taken out in a round is the one you see.

    On the example, the rounds see these levels:

    level [1]
    level [2, 3]
    level [5, 4]

    The last value of each line is the answer: 1, 3 and 4.

    Step by step

    1. Create result, an empty [Int]. It collects one value per level.
    2. Handle the empty tree. guard let root = root else { return result } unwraps the optional. Below the guard, root is a plain TreeNode, and an empty tree has already returned [].
    3. Create the queue as an array holding root, plus an index head that starts at 0. The waiting nodes are the ones from head to the end of the array. Taking a node out means reading queue[head] and moving head forward, which costs O(1). Calling removeFirst() would shift every remaining element each time.
    4. Loop while head < queue.count, that is, while some node is still waiting. Each pass of this loop handles one whole level.
    5. Store the level's size, queue.count - head, in the constant levelSize. At this moment the waiting nodes are exactly one level, so this is how many nodes to take out in this round. The children appended during the round make queue.count grow, which is why the size has to be read once, before the inner loop.
    6. Loop over 0..<levelSize with i. The loop stops before it reaches the children that were added during this round.
    7. Take the next node: read queue[head] into node and do head += 1.
    8. Record the last node of the level. When i == levelSize - 1, append node.val to result. The node may come from the left subtree, and that is fine, because we look at levels and not at the right children only.
    9. Queue the left child with if let left = node.left, appending left inside the block.
    10. Queue the right child the same way, right after the left one, so that the rightmost node of the next level is the last to enter.
    11. Return result after the loops.

    The solution in Swift

    class Solution {
        func rightSideView(_ root: TreeNode?) -> [Int] {
            var result = [Int]()
            guard let root = root else {
                return result
            }
            var queue = [root]
            var head = 0
            while head < queue.count {
                let levelSize = queue.count - head
                for i in 0..<levelSize {
                    let node = queue[head]
                    head += 1
                    if i == levelSize - 1 {
                        result.append(node.val)
                    }
                    if let left = node.left {
                        queue.append(left)
                    }
                    if let right = node.right {
                        queue.append(right)
                    }
                }
            }
            return result
        }
    }
    

    The Swift details worth a closer look:

    • guard let root = root. The new constant has the same name as the parameter and hides it for the rest of the method. A guard body must leave the method, so after it the code can rely on root being non-nil.
    • var queue = [root]. Swift infers [TreeNode]. TreeNode is a class, so the array holds references to the nodes and nothing is copied when they are appended.
    • if let left = node.left. left and right are optionals. if let runs its block only when a child exists, so no nil ever enters the queue.
    • 0..<levelSize. The half-open range stops before levelSize. Because levelSize is at least 1 inside the loop, the range is never reversed, which in Swift would be a run-time error.

    Complexity

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

    Space: O(n) extra. The array keeps every node it has ever held, because head only moves forward and nothing is removed, so it ends with all n nodes. The part that is waiting at any moment is at most about two levels, which is also O(n) in the worst case. Without the head trick, the queue would hold only the waiting nodes, but each removeFirst() would cost O(n).

    Edge cases and common mistakes

    • Empty tree. root is nil, the guard returns [], and the queue code is never reached.
    • Single node. The tree [7] gives [7]: one level, and its only node is also its last.
    • Only following right children. On [1,2,3,4] it returns [1, 3] and misses 4. On [1,2,nil,3,nil,4] it returns [1].
    • Forgetting head += 1. The same node is read again and again, and the outer loop never ends.
    • Repeated values. Values may repeat, and that doesn't matter: the method looks at positions in the tree and never compares values. The tree [5,5,5,5,5] gives [5, 5, 5], one 5 per level.

    Testing

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