API Kernels: TreeTraversalDFS, TreeTraversalBFSCore Mechanism: Process tree nodes in specific orders using recursion (DFS) or queue (BFS).
This document presents the canonical tree traversal templates covering DFS (preorder, inorder, postorder), BFS (level-order), and tree property computation patterns. Each implementation includes both recursive and iterative approaches.
# Preorder: Node β Left β Right (top-down)defpreorder(root):ifnotroot:returnvisit(root)preorder(root.left)preorder(root.right)# Inorder: Left β Node β Right (BST sorted order)definorder(root):ifnotroot:returninorder(root.left)visit(root)inorder(root.right)# Postorder: Left β Right β Node (bottom-up)defpostorder(root):ifnotroot:returnpostorder(root.left)postorder(root.right)visit(root)
fromcollectionsimportdequedeflevel_order(root):ifnotroot:return[]result=[]queue=deque([root])whilequeue:level=[]for_inrange(len(queue)):# Process entire levelnode=queue.popleft()level.append(node.val)ifnode.left:queue.append(node.left)ifnode.right:queue.append(node.right)result.append(level)returnresult
# Pattern 1: Return single value (e.g., height)defheight(root):ifnotroot:return0return1+max(height(root.left),height(root.right))# Pattern 2: Return with early termination (e.g., is balanced)defis_balanced(root):defcheck(node):ifnotnode:return0left=check(node.left)ifleft==-1:return-1# Early terminationright=check(node.right)ifright==-1:return-1ifabs(left-right)>1:return-1return1+max(left,right)returncheck(root)!=-1# Pattern 3: Track path through recursion (e.g., max path sum)defmax_path(root):result=[float('-inf')]defdfs(node):ifnotnode:return0left=max(0,dfs(node.left))# Can choose to not take pathright=max(0,dfs(node.right))result[0]=max(result[0],left+node.val+right)# Path through nodereturnnode.val+max(left,right)# Return single branchdfs(root)returnresult[0]
# Pattern: tree_dfs_inorder# See: docs/patterns/tree/templates.md Section 1classSolution:definorderTraversal(self,root:Optional[TreeNode])->List[int]:""" Inorder traversal: Left β Node β Right. Key Insight: - Recursive: natural left-first, then visit, then right - Iterative: use stack, go left as far as possible """result:list[int]=[]defdfs(node:Optional[TreeNode])->None:ifnotnode:returndfs(node.left)result.append(node.val)dfs(node.right)dfs(root)returnresult
definorderTraversal(self,root:Optional[TreeNode])->List[int]:"""Iterative inorder using explicit stack."""result:list[int]=[]stack:list[TreeNode]=[]curr=rootwhilecurrorstack:# Go left as far as possiblewhilecurr:stack.append(curr)curr=curr.left# Process nodecurr=stack.pop()result.append(curr.val)# Move to right subtreecurr=curr.rightreturnresult
Tree: 1
\
2
/
3
Recursive:
1. dfs(1): dfs(None), visit(1), dfs(2)
2. dfs(2): dfs(3), visit(2), dfs(None)
3. dfs(3): dfs(None), visit(3), dfs(None)
Order: 3 (left of 2), 2 (middle), 1 β wait, let me fix:
Actually: inorder visits nodes in order: left subtree, node, right subtree
1. At node 1: left is None, visit 1, go right to 2
2. At node 2: left is 3, so first visit 3
3. At node 3: left is None, visit 3, right is None
4. Back to 2: visit 2, right is None
Result: [1, 3, 2]
3. Binary Tree Level Order Traversal (LeetCode 102)¶
Problem: Return level-order traversal as list of lists. Invariant: Process all nodes at depth d before depth d+1. Role: BASE TEMPLATE for BFS traversal.
# Pattern: tree_bfs_level_order# See: docs/patterns/tree/templates.md Section 2fromcollectionsimportdequeclassSolution:deflevelOrder(self,root:Optional[TreeNode])->List[List[int]]:""" Level-order traversal using BFS. Key Insight: - Use queue for BFS - Process entire level at once using len(queue) - Add children to queue for next level """ifnotroot:return[]result:list[list[int]]=[]queue:deque[TreeNode]=deque([root])whilequeue:level:list[int]=[]level_size=len(queue)for_inrange(level_size):node=queue.popleft()level.append(node.val)ifnode.left:queue.append(node.left)ifnode.right:queue.append(node.right)result.append(level)returnresult
deflevelOrder(self,root:Optional[TreeNode])->List[List[int]]:"""Level-order using DFS with depth tracking."""result:list[list[int]]=[]defdfs(node:Optional[TreeNode],depth:int)->None:ifnotnode:returnifdepth==len(result):result.append([])result[depth].append(node.val)dfs(node.left,depth+1)dfs(node.right,depth+1)dfs(root,0)returnresult
Problem: Find the maximum depth (height) of a binary tree. Invariant: Depth = 1 + max(left_depth, right_depth). Role: BASE TEMPLATE for tree property computation.
# Pattern: tree_property_validation# See: docs/patterns/tree/templates.md Section 4classSolution:defisBalanced(self,root:Optional[TreeNode])->bool:""" Check if tree is height-balanced. Key Insight: - Compute height bottom-up - Return -1 to signal imbalance (early termination) - Avoid recomputing heights """defcheck(node:Optional[TreeNode])->int:"""Return height if balanced, -1 if not."""ifnotnode:return0left=check(node.left)ifleft==-1:return-1# Early terminationright=check(node.right)ifright==-1:return-1ifabs(left-right)>1:return-1return1+max(left,right)returncheck(root)!=-1
defisBalanced(self,root:Optional[TreeNode])->bool:"""Naive: recomputes height at each level."""ifnotroot:returnTruedefheight(node):ifnotnode:return0return1+max(height(node.left),height(node.right))# Check this node and recursereturn(abs(height(root.left)-height(root.right))<=1andself.isBalanced(root.left)andself.isBalanced(root.right))
Problem: Find the longest path between any two nodes. Invariant: Diameter through node = left_height + right_height. Role: VARIANT combining heights for path problems.
# Pattern: tree_path_computation# See: docs/patterns/tree/templates.md Section 5classSolution:defdiameterOfBinaryTree(self,root:Optional[TreeNode])->int:""" Longest path between any two nodes (in edges). Key Insight: - For each node, longest path through it = left_height + right_height - Track maximum during height computation - Return height for recursion, track diameter separately """self.diameter=0defheight(node:Optional[TreeNode])->int:ifnotnode:return0left_h=height(node.left)right_h=height(node.right)# Update diameter (path through this node)self.diameter=max(self.diameter,left_h+right_h)# Return height for parent's computationreturn1+max(left_h,right_h)height(root)returnself.diameter
defdiameterOfBinaryTree(self,root:Optional[TreeNode])->int:"""Using nonlocal or list to track max."""result=[0]defheight(node):ifnotnode:return0left=height(node.left)right=height(node.right)result[0]=max(result[0],left+right)return1+max(left,right)height(root)returnresult[0]
Problem: Find the maximum path sum where path visits each node at most once. Invariant: Max through node = node.val + max(0, left) + max(0, right). Role: HARD VARIANT of diameter pattern with values.
# Pattern: tree_path_sum# See: docs/patterns/tree/templates.md Section 6classSolution:defmaxPathSum(self,root:Optional[TreeNode])->int:""" Maximum path sum in binary tree. Key Insight: - At each node, consider it as path apex - Path sum through node = node.val + left_gain + right_gain - Gain from subtree = max(0, subtree_max) (can skip negative) - Return single branch max (can only go one direction to parent) """self.max_sum=float('-inf')defmax_gain(node:Optional[TreeNode])->int:ifnotnode:return0# Max gain from left/right (ignore negative paths)left_gain=max(0,max_gain(node.left))right_gain=max(0,max_gain(node.right))# Path sum if this node is apexpath_sum=node.val+left_gain+right_gainself.max_sum=max(self.max_sum,path_sum)# Return max single-branch gain for parentreturnnode.val+max(left_gain,right_gain)max_gain(root)returnself.max_sum
# Consider tree with negative branch:# 1# /# -5## Without max(0, ...):# path through 1 would be 1 + (-5) = -4## With max(0, ...):# left_gain = max(0, -5) = 0# path through 1 = 1 + 0 = 1# We "skip" the negative subtree
# PATTERN 1: Simple property (height, count)defproperty(node):ifnotnode:returnBASE_VALUEleft=property(node.left)right=property(node.right)returnCOMBINE(node.val,left,right)# PATTERN 2: Validation with early terminationdefvalidate(node):ifnotnode:returnVALID_BASEleft=validate(node.left)ifnotleft:returnINVALID# Early terminationright=validate(node.right)ifnotright:returnINVALIDreturnCHECK(node,left,right)# PATTERN 3: Path tracking (diameter, max path sum)defpath_property(node):ifnotnode:returnBASEleft=path_property(node.left)right=path_property(node.right)UPDATE_GLOBAL(node,left,right)# Track max pathreturnSINGLE_BRANCH(node,left,right)# Return for parent
What should recursion return?
1. Single value (height, count)
return 1 + max(left, right)
2. Boolean validation
return left and right and CHECK
3. Sentinel for invalid
if invalid: return -1
return valid_value
4. Path contribution
UPDATE_GLOBAL(left + right + node)
return node + max(left, right)