Recursion, binary trees, and a note on debugging

Completed binary tree traversals and recursive implementations. The key insight was drawing call stacks by hand.

Recursion started to feel natural this week after spending time manually tracing call stacks for small inputs. The mechanical understanding — “this frame waits while that frame resolves” — preceded the intuitive understanding, which I think is the correct order.

Binary tree traversals

Implemented in-order, pre-order, and post-order traversals both recursively and iteratively. The recursive versions are trivially concise. The iterative versions require explicit stack management and are more instructive for understanding what the runtime actually does.

In-order traversal of a BST yields sorted output, which is a useful property for validation: if your in-order traversal isn’t sorted, your tree invariant is broken.

On debugging recursive functions

print statements are inadequate for debugging recursion because the output is a flat list that doesn’t represent the call hierarchy. What works better: adding an indent parameter that increments with depth, so each recursive call prints with increasing indentation. The visual structure of the output mirrors the call tree.

def traverse(node, depth=0):
    if not node:
        return
    prefix = "  " * depth
    print(f"{prefix}{node.val}")
    traverse(node.left, depth + 1)
    traverse(node.right, depth + 1)

Small technique, large improvement in comprehension.