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.
Example 1:
Input: root = [1,2,3,null,5,null,4] Output: [1,3,4]
Example 2:
Input: root = [1,null,3] Output: [1,3]
Example 3:
Input: root = [] Output: []
Constraints:
[0, 100]
.-100 <= Node.val <= 100
The description was taken from https://leetcode.com/problems/binary-tree-right-side-view/.
# Iterative BFS
# O(N) Time, O(Width) Space
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
if not root:
return root
output = []
def bfs(root:TreeNode) -> None:
queue = collections.deque([root])
while queue:
right_most = None
level_width = len(queue)
for _ in range(level_width):
node = queue.popleft()
right_most = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if right_most:
output.append(right_most.val)
bfs(root)
return output
# Recursive DFS
# O(N) Time, O(Depth) Space
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
if not root:
return root
output = []
def dfs(node: TreeNode, level: int) -> None:
if not root:
return
if level == len(output):
output.append(node.val)
if node.right:
dfs(node.right, level + 1)
if node.left:
dfs(node.left, level + 1)
dfs(root, 0)
return output
Let's start with a more straightforward solution, the iterative one.
We can do a traditional BFS but we will set a right_most
pointer to each node we process within a given level. By doing this, the last node seen in each level will be the one farthest on the right side.
First off, if there is no root that was input, we have nothing to see on the right side so we are done.
if not root:
return root
Otherwise, we will start by initializing our output array.
output = []
Next, we will create our bfs
function.
def bfs(root:TreeNode) -> None:
We'll initialize a queue from a deque and push the root into it.
queue = collections.deque([root])
Then, we will create a loop that will run while the queue still has nodes we need to process.
while queue:
Before processing any nodes, we will first reset the right_most
pointer to none and count the nodes that are currently in the queue so that we know how long our iteration should be for the given level.
right_most = None
level_width = len(queue)
Now we can start writing the main BFS logic.
For each node in the current level, we will pop the node off of the queue, label it as the right_most
node of the current level, append its value to the output array and then push all of its children into the queue.
for _ in range(level_width):
node = queue.popleft()
right_most = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
If the right_most
node was set in the iteration, we will append the value of the node it was last pointed at.
if right_most:
output.append(right_most.val)
Now, all we need to do is make the initial function call by passing the root.
bfs(root)
Once the BFS is complete, we will return the output array of all the rightmost values.
return output
The DFS solution is a little trickier in theory but easier to implement.
The basic idea is we will only have one node per level that we would be able to see from the right-side view. From that requirement, we can use the length of the output as an indicator for whether or not the farthest right node has been picked yet for the given level.
We'll run the preorder DFS on the right child first of each node so that we favor the right side of the tree in our search. If the length of the output is the same as the level, then that means we haven't chosen a node from the current level yet so we can append the first node seen in our search since the DFS path will go to the farthest path down the right side of the tree before moving toward any left paths.
First off, if there is no root that was input, we have nothing to see on the right side so we are done.
if not root:
return root
Otherwise, we will start by initializing our output array.
output = []
Next, we will create our recursive dfs
function.
The function will take a level integer so that we can keep track of the level each node is on, and it will also take the node it is being called on.
def dfs(node: TreeNode, level: int) -> None:
If the current node is null, we have reached the end of a valid path so we will return.
if not root:
return
Otherwise, we will check if the length of the output is the same as the level value.
For example, if the length of the output is zero and we are on level zero, the root level, we know that we haven't picked a node from this level yet so we will pick the first node we see in our DFS.
Notice we put the base case before the function calls in the function. This ensures that the DFS is picking values as it moves down the search.
if level == len(output):
output.append(node.val)
After appending a value or not, we will continue the DFS on the right node first then the left while incrementing the level value by one.
if node.right:
dfs(node.right, level + 1)
if node.left:
dfs(node.left, level + 1)
Now that we have the function built, we just need to make the initial call by passing the root and the initial level value of zero.
Once the DFS is complete, we will return all of the rightmost values.
dfs(root, 0)
return output