Given the root
of a binary tree, return the bottom-up level order traversal of its nodes' values. (i.e., from left to right, level by level from leaf to root).
Example 1:
Input: root = [3,9,20,null,null,15,7] Output: [[15,7],[9,20],[3]]
Example 2:
Input: root = [1] Output: [[1]]
Example 3:
Input: root = [] Output: []
Constraints:
[0, 2000]
.-1000 <= Node.val <= 1000
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
We want to visit every level of the tree and collect the nodes, but starting from the bottom. The brute force way is to visit each node and figure out what level it is on, then sort the nodes by level in reverse order.
Here's how the algorithm would work step-by-step:
def binary_tree_level_order_traversal_ii(root):
if not root:
return []
node_levels = []
def get_level(node, current_level):
if not node:
return
node_levels.append((node, current_level))
get_level(node.left, current_level + 1)
get_level(node.right, current_level + 1)
# Traverse the tree to determine the level of each node.
get_level(root, 0)
level_map = {}
for node, level in node_levels:
if level not in level_map:
level_map[level] = []
level_map[level].append(node.val)
# We must sort levels to guarantee reverse level order.
sorted_levels = sorted(level_map.keys(), reverse=True)
result = []
# Build result based on levels.
for level in sorted_levels:
result.append(level_map[level])
return result
We need to visit every level of the tree and organize the nodes at each level. The trick is to process the levels from top to bottom, but then reverse the order of the final result to get the bottom levels first. This lets us build the levels in the natural way, but present them in the requested order.
Here's how the algorithm would work step-by-step:
from collections import deque
def binaryTreeLevelOrderTraversal2(root):
if not root:
return []
levels = []
queue = deque([root])
while queue:
level_size = len(queue)
current_level_values = []
# Process all nodes at the current level
for _ in range(level_size):
node = queue.popleft()
current_level_values.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
levels.append(current_level_values)
# Reverse the order of levels
levels.reverse()
return levels
Case | How to Handle |
---|---|
Null root | Return an empty list immediately since there is no tree to traverse. |
Single node tree | Return a list containing a list with the single node's value. |
Perfectly balanced tree | The algorithm should process this efficiently, demonstrating optimal performance. |
Completely unbalanced (skewed) tree (left or right) | The algorithm should handle the varying level sizes gracefully and depth of tree without stack overflow. |
Tree with duplicate values | The level order traversal should correctly include all duplicate values at their respective levels. |
Very large tree (memory constraints) | Consider space complexity; for extremely large trees, using an iterative approach with a queue might be preferred to minimize memory usage compared to recursive approaches which might cause stackoverflow. |
Tree with negative values | The algorithm should correctly process negative values as valid node data without any type overflow. |
Integer overflow during tree processing if node values are very large | Ensure operations on node values (e.g., summing for calculations) are done within integer limits by selecting appropriate data structures. |