Functions
Recursion
Functions calling themselves with base cases and memoization
Interview: Extremely common in interviews — tree traversal, divide-and-conquer, and dynamic programming
Recursion is when a function calls itself to solve a problem by breaking it into smaller subproblems. It's a fundamental technique in computer science, especially for tree/graph traversal, divide-and-conquer algorithms, and problems with naturally recursive structure. Mastering recursion is essential for coding interviews.
Anatomy of a Recursive Function
- Base case: The condition that stops recursion — without it, infinite recursion causes stack overflow
- Recursive case: The function calls itself with a smaller/simpler input, moving toward the base case
- Progress: Each recursive call must make progress toward the base case — otherwise infinite loop
- Return: Results are combined as calls unwind from the base case back to the original call
Types of Recursion
- Direct: Function calls itself — most common pattern
- Mutual: Two functions call each other —
is_evencallsis_oddand vice versa - Tail recursion: Recursive call is the last operation — Python does NOT optimize this
- Multiple recursion: Function makes more than one recursive call — Fibonacci, tree traversal
Memoization: Making Recursion Fast
Naive recursive Fibonacci is O(2^n). With memoization (caching results), it becomes O(n). Use @lru_cache from functools for automatic memoization, or pass a memo dict manually. This is the bridge between recursion and dynamic programming.
Python Recursion Limits
- Default limit: Python's default recursion depth is ~1000 — exceeding it raises RecursionError
- sys.setrecursionlimit(): Increase the limit, but be careful — too deep causes segfaults
- Iterative alternative: Any recursion can be converted to iteration using an explicit stack
- Performance: Each recursive call adds a stack frame — recursion uses O(n) memory for O(n) depth
When to Use Recursion
- Tree/graph traversal: DFS, binary tree operations, directory walking
- Divide-and-conquer: Merge sort, quicksort, binary search
- Backtracking: Permutations, combinations, N-queens, maze solving
- Mathematical: Factorial, GCD, power, Fibonacci
Stack Overflow Prevention
Always verify your base case is correct and that each recursive call moves toward it. Add a maximum depth parameter for safety: def recurse(n, depth=0, max_depth=1000). In production code, prefer iterative solutions for deep recursions.
Use Cases
Tree and graph traversal (DFS, inorder/preorder/postorder)
Divide-and-conquer algorithms (merge sort, quicksort, binary search)
Backtracking problems (permutations, combinations, N-queens)
Dynamic programming with memoization (knapsack, longest subsequence)
Processing recursively structured data (nested JSON, file systems)
Common Mistakes
Missing or incorrect base case — causes infinite recursion and RecursionError
Not making progress toward base case — each call must reduce the problem size
Ignoring Python's ~1000 recursion limit — use sys.setrecursionlimit() or convert to iteration
Not using memoization for overlapping subproblems — exponential time without it
Choosing recursion when iteration is simpler — Python doesn't optimize tail recursion