questions B (backspace - burst-balloons)
This commit is contained in:
194
backend/data/questions/binary-tree-right-side-view.yaml
Normal file
194
backend/data/questions/binary-tree-right-side-view.yaml
Normal file
@@ -0,0 +1,194 @@
|
||||
title: Binary Tree Right Side View
|
||||
slug: binary-tree-right-side-view
|
||||
difficulty: medium
|
||||
leetcode_id: 199
|
||||
leetcode_url: https://leetcode.com/problems/binary-tree-right-side-view/
|
||||
categories:
|
||||
- trees
|
||||
patterns:
|
||||
- bfs
|
||||
- dfs
|
||||
- tree-traversal
|
||||
|
||||
description: |
|
||||
Given the `root` of a binary tree, imagine yourself standing on the **right side** of it, return *the values of the nodes you can see ordered from top to bottom*.
|
||||
|
||||
constraints: |
|
||||
- The number of nodes in the tree is in the range `[0, 100]`
|
||||
- `-100 <= Node.val <= 100`
|
||||
|
||||
examples:
|
||||
- input: "root = [1,2,3,null,5,null,4]"
|
||||
output: "[1,3,4]"
|
||||
explanation: "Standing on the right side, you see node 1 at level 0, node 3 at level 1 (it's rightmost), and node 4 at level 2 (it's rightmost even though it's a child of node 3)."
|
||||
- input: "root = [1,2,3,4,null,null,null,5]"
|
||||
output: "[1,3,4,5]"
|
||||
explanation: "At each level, you see the rightmost node: 1, then 3, then 4 (left child of 2, but rightmost at that level since 3 has no children at that depth), then 5."
|
||||
- input: "root = [1,null,3]"
|
||||
output: "[1,3]"
|
||||
explanation: "The tree only has right children, so you see both nodes from the right side."
|
||||
- input: "root = []"
|
||||
output: "[]"
|
||||
explanation: "An empty tree has no nodes to see."
|
||||
|
||||
explanation:
|
||||
intuition: |
|
||||
Imagine you're standing to the right of a building where each floor has rooms arranged left to right. From your vantage point, you can only see the **rightmost room on each floor** — the other rooms are hidden behind it.
|
||||
|
||||
A binary tree works similarly when viewed from the right side. At each *level* (or depth) of the tree, only the **rightmost node** is visible. All other nodes at that level are obscured.
|
||||
|
||||
The key insight is that "right side view" means collecting **one node per level** — specifically, the last node encountered when traversing that level from left to right. This naturally suggests a **level-order traversal** (BFS) where we process nodes level by level and remember the last node at each level.
|
||||
|
||||
Alternatively, with DFS, we can traverse the tree visiting the right subtree before the left. The first node we encounter at each new depth becomes the visible node from the right.
|
||||
|
||||
approach: |
|
||||
We solve this using a **Level-Order Traversal (BFS)** approach:
|
||||
|
||||
**Step 1: Handle edge case**
|
||||
|
||||
- If `root` is `None`, return an empty list — there are no nodes to see
|
||||
|
||||
|
||||
|
||||
**Step 2: Initialise data structures**
|
||||
|
||||
- `result`: Empty list to store the right side view
|
||||
- `queue`: Initialise with the root node for BFS traversal
|
||||
|
||||
|
||||
|
||||
**Step 3: Process level by level**
|
||||
|
||||
- While the queue is not empty, determine the number of nodes at the current level (`level_size`)
|
||||
- Iterate through all nodes at this level
|
||||
- For each node, add its children (left then right) to the queue for the next level
|
||||
- The **last node** processed in each level is the rightmost — add its value to the result
|
||||
|
||||
|
||||
|
||||
**Step 4: Return the result**
|
||||
|
||||
- After processing all levels, `result` contains the right side view from top to bottom
|
||||
|
||||
|
||||
|
||||
The BFS approach naturally processes nodes left-to-right at each level, so the last node we see at each level is exactly what we'd see from the right side.
|
||||
|
||||
common_pitfalls:
|
||||
- title: Confusing Right Side View with Right Children Only
|
||||
description: |
|
||||
A common mistake is thinking the right side view only includes nodes that are right children. This is incorrect.
|
||||
|
||||
Consider a tree where node 2 is a left child but has no sibling on the right at its level — node 2 would be visible from the right side. For example, in `[1,2,null]`, both 1 and 2 are visible from the right even though 2 is a left child.
|
||||
|
||||
The right side view includes the **rightmost node at each level**, regardless of whether it's a left or right child.
|
||||
wrong_approach: "Only collecting right children"
|
||||
correct_approach: "Collecting the last node at each level via BFS"
|
||||
|
||||
- title: Not Processing Full Levels in BFS
|
||||
description: |
|
||||
When using BFS, you must process **all nodes at a level** before moving to the next level. A common bug is to simply take the last element from the queue without tracking level boundaries.
|
||||
|
||||
The fix is to record `level_size = len(queue)` at the start of each level and iterate exactly that many times. This ensures you correctly identify where each level ends.
|
||||
wrong_approach: "Processing queue without tracking level boundaries"
|
||||
correct_approach: "Using level_size to process exactly one level at a time"
|
||||
|
||||
- title: Wrong DFS Order
|
||||
description: |
|
||||
When using DFS for this problem, you must visit the **right subtree before the left**. If you visit left first, the first node you see at each depth will be the leftmost, not the rightmost.
|
||||
|
||||
The DFS approach works by recording the first node encountered at each new depth — so visiting right-first ensures we see the right side view.
|
||||
wrong_approach: "DFS visiting left subtree before right"
|
||||
correct_approach: "DFS visiting right subtree before left"
|
||||
|
||||
key_takeaways:
|
||||
- "**Level-order traversal** (BFS) is ideal when you need to process nodes level by level or find the first/last node at each depth"
|
||||
- "**Right side view = rightmost at each level**, not just right children — understanding this distinction is crucial"
|
||||
- "**DFS alternative**: Visit right-before-left and track depth to achieve the same result with O(h) space instead of O(w)"
|
||||
- "This pattern extends to **left side view** (take first node per level) and other level-based tree problems"
|
||||
|
||||
time_complexity: "O(n). We visit each node exactly once during the BFS traversal."
|
||||
space_complexity: "O(w) where w is the maximum width of the tree. In the worst case (a complete binary tree), the last level can have up to n/2 nodes, so this is O(n) in the worst case."
|
||||
|
||||
solutions:
|
||||
- approach_name: BFS Level-Order Traversal
|
||||
is_optimal: true
|
||||
code: |
|
||||
from collections import deque
|
||||
from typing import Optional
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self, val=0, left=None, right=None):
|
||||
self.val = val
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
def right_side_view(root: Optional[TreeNode]) -> list[int]:
|
||||
# Edge case: empty tree
|
||||
if not root:
|
||||
return []
|
||||
|
||||
result = []
|
||||
queue = deque([root])
|
||||
|
||||
while queue:
|
||||
# Number of nodes at current level
|
||||
level_size = len(queue)
|
||||
|
||||
for i in range(level_size):
|
||||
node = queue.popleft()
|
||||
|
||||
# Add children for next level (left before right)
|
||||
if node.left:
|
||||
queue.append(node.left)
|
||||
if node.right:
|
||||
queue.append(node.right)
|
||||
|
||||
# Last node in level is the rightmost (visible from right)
|
||||
if i == level_size - 1:
|
||||
result.append(node.val)
|
||||
|
||||
return result
|
||||
explanation: |
|
||||
**Time Complexity:** O(n) — Every node is visited exactly once.
|
||||
|
||||
**Space Complexity:** O(w) — The queue holds at most one level of nodes. For a complete binary tree, the widest level has ~n/2 nodes.
|
||||
|
||||
We use BFS to traverse level by level. At each level, we process all nodes left-to-right, adding their children to the queue. The last node we process at each level is the rightmost, which is what we'd see from the right side.
|
||||
|
||||
- approach_name: DFS Right-First Traversal
|
||||
is_optimal: false
|
||||
code: |
|
||||
from typing import Optional
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self, val=0, left=None, right=None):
|
||||
self.val = val
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
def right_side_view(root: Optional[TreeNode]) -> list[int]:
|
||||
result = []
|
||||
|
||||
def dfs(node: Optional[TreeNode], depth: int) -> None:
|
||||
if not node:
|
||||
return
|
||||
|
||||
# First time reaching this depth? This node is visible from right
|
||||
if depth == len(result):
|
||||
result.append(node.val)
|
||||
|
||||
# Visit right subtree first to see rightmost nodes first
|
||||
dfs(node.right, depth + 1)
|
||||
dfs(node.left, depth + 1)
|
||||
|
||||
dfs(root, 0)
|
||||
return result
|
||||
explanation: |
|
||||
**Time Complexity:** O(n) — Every node is visited exactly once.
|
||||
|
||||
**Space Complexity:** O(h) — The recursion stack uses space proportional to the tree height. For a balanced tree h = log(n), for a skewed tree h = n.
|
||||
|
||||
We use DFS, visiting the right subtree before the left. The first time we reach a new depth, that node must be the rightmost at that level (since we explore right-first). We track this by comparing `depth` with `len(result)`.
|
||||
|
||||
This approach uses less space than BFS for tall, narrow trees but more space for short, wide trees.
|
||||
Reference in New Issue
Block a user