Traverse trees using preorder, inorder, or postorder to solve tree-based problems
Tree DFS explores as far as possible along each branch before backtracking. Three main traversal orders exist: preorder (root-left-right), inorder (left-root-right), and postorder (left-right-root). Each order is useful for different problem types.
Use DFS for tree traversal problems, finding paths, calculating tree properties (height, diameter), validating tree structures, or when you need to process nodes in a specific order. Particularly effective for problems requiring bottom-up or top-down information flow.
Process root before visiting children, useful for copying trees
def preorder(root, result=[]):
if not root:
return
# Process root first
result.append(root.val)
# Then left subtree
preorder(root.left, result)
# Then right subtree
preorder(root.right, result)
return result
# Iterative version
def preorder_iterative(root):
if not root:
return []
result = []
stack = [root]
while stack:
node = stack.pop()
result.append(node.val)
# Push right first (so left is processed first)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return result
Process left subtree, then root, then right subtree
def inorder(root, result=[]):
if not root:
return
# Left subtree first
inorder(root.left, result)
# Process root
result.append(root.val)
# Right subtree last
inorder(root.right, result)
return result
# Iterative version
def inorder_iterative(root):
result = []
stack = []
current = root
while current or stack:
# Go to leftmost node
while current:
stack.append(current)
current = current.left
# Process node
current = stack.pop()
result.append(current.val)
# Move to right subtree
current = current.right
return result
Process children before root, useful for bottom-up calculations
def postorder(root, result=[]):
if not root:
return
# Left subtree first
postorder(root.left, result)
# Right subtree second
postorder(root.right, result)
# Process root last
result.append(root.val)
return result
# Iterative version (using two stacks)
def postorder_iterative(root):
if not root:
return []
result = []
stack1 = [root]
stack2 = []
while stack1:
node = stack1.pop()
stack2.append(node)
if node.left:
stack1.append(node.left)
if node.right:
stack1.append(node.right)
while stack2:
result.append(stack2.pop().val)
return result
def dfs_preorder(root):
if not root:
return
# Process current node (preorder)
result.append(root.val)
# Recurse on children
dfs_preorder(root.left)
dfs_preorder(root.right)
def dfs_inorder(root):
if not root:
return
dfs_inorder(root.left)
result.append(root.val) # Process (inorder)
dfs_inorder(root.right)
def dfs_postorder(root):
if not root:
return
dfs_postorder(root.left)
dfs_postorder(root.right)
result.append(root.val) # Process (postorder)
Visit each node exactly once, where n is the number of nodes in the tree.
Recursion stack depth equals tree height h. O(log n) for balanced trees, O(n) for skewed trees.