Given the root
of a binary tree, return the preorder traversal of its nodes' values.
Example 1:
Input: root = [1,null,2,3]
Output: [1,2,3]
Explanation:
Example 2:
Input: root = [1,2,3,4,5,null,8,null,null,6,7,9]
Output: [1,2,4,5,6,7,3,8,9]
Explanation:
Example 3:
Input: root = []
Output: []
Example 4:
Input: root = [1]
Output: [1]
Constraints:
[0, 100]
.-100 <= Node.val <= 100
Follow up: Recursive solution is trivial, could you do it iteratively?
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:
The brute force approach to traversing a binary tree in preorder is like meticulously visiting every node in the tree in a specific order. We visit the current node first, then its left child, and finally its right child. This is done recursively, ensuring that we explore all possibilities to get the correct traversal sequence.
Here's how the algorithm would work step-by-step:
def preorder_traversal_brute_force(root):
result_list = []
def traverse_tree(node):
if not node:
return
# Visit the current node and record its value.
result_list.append(node.val)
# Recursively traverse the left subtree.
traverse_tree(node.left)
# Recursively traverse the right subtree.
traverse_tree(node.right)
traverse_tree(root)
return result_list
We need to visit the root of a tree, then the left side, then the right side. The most efficient way to do this uses the computer's memory to keep track of where we need to go next as we walk through the tree.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def preorder_traversal(root):
result_list = []
node_stack = []
current_node = root
while current_node or node_stack:
# Traverse left as far as possible and record the values
while current_node:
result_list.append(current_node.val)
# Store current node to potentially revisit it later
node_stack.append(current_node)
current_node = current_node.left
# Backtrack and go right
current_node = node_stack.pop()
# If there's a right child, explore it
if current_node.right:
current_node = current_node.right
else:
current_node = None
return result_list
Case | How to Handle |
---|---|
Null root node | Return an empty list when the root is null, as there's nothing to traverse. |
Tree with only a root node | Return a list containing only the root's value, since that's the entire traversal. |
Completely skewed tree (left or right) | Recursive solution's call stack will grow linearly with tree height, potentially leading to stack overflow for extremely deep trees; iterative solutions can avoid this. |
Large tree (potential memory limitations) | Iterative solutions might be preferable to recursive solutions to avoid excessive stack usage for very large trees which could exhaust memory resources. |
Tree with duplicate values | Duplicate node values don't affect the preorder traversal algorithm's correctness, so it functions as intended. |
Tree with negative or zero node values | Negative or zero values are valid node values and should be treated the same as any other integer value. |
Integer overflow in node values (if applicable) | If node values can exceed the maximum integer value, consider using a larger data type or employing modular arithmetic if appropriate to prevent integer overflows. |
Recursive solution exceeds maximum recursion depth | For very deep trees, the recursive solution may hit the maximum recursion depth, so an iterative solution using a stack is a good alternative. |