Recursion

A function is recursive when it calls itself. Every recursive function needs a base case (when to stop) and a recursive case (how to break the problem down).

Why It Matters

Recursion is the natural way to work with trees, graphs, and many algorithms like DFS, dynamic programming, and backtracking. If you can’t think recursively, entire categories of problems become much harder.

The Pattern

def solve(problem):
    if problem is trivial:     # base case
        return answer
    smaller = break_down(problem)
    return combine(solve(smaller))  # recursive case

Code Examples

# Factorial: n! = n * (n-1) * ... * 1
def factorial(n: int) -> int:
    if n <= 1:          # base case
        return 1
    return n * factorial(n - 1)  # recursive case
 
assert factorial(5) == 120  # 5 * 4 * 3 * 2 * 1
 
# Fibonacci (naive — O(2^n), see DP for better)
def fib(n: int) -> int:
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)
 
assert fib(10) == 55
 
# Binary search (recursive version)
def binary_search(arr: list, target: int, lo: int = 0, hi: int = None) -> int:
    if hi is None:
        hi = len(arr) - 1
    if lo > hi:
        return -1
    mid = (lo + hi) // 2
    if arr[mid] == target:
        return mid
    elif arr[mid] < target:
        return binary_search(arr, target, mid + 1, hi)
    else:
        return binary_search(arr, target, lo, mid - 1)
 
assert binary_search([1, 3, 5, 7, 9], 7) == 3

The Call Stack

Each recursive call adds a frame to the call stack. Too many calls = RecursionError (stack overflow).

factorial(4)
  → 4 * factorial(3)
    → 3 * factorial(2)
      → 2 * factorial(1)
        → returns 1         # base case hit
      → returns 2 * 1 = 2
    → returns 3 * 2 = 6
  → returns 4 * 6 = 24

Python’s default recursion limit is 1000. For deep recursion, convert to iteration or increase the limit with sys.setrecursionlimit().

Recursion vs Iteration

RecursionIteration
Elegant for tree/graph problemsUsually faster (no call overhead)
Uses O(n) stack spaceUses O(1) extra space
Risk of stack overflowNo stack overflow risk
Natural for divide-and-conquerNatural for linear scans

Rule of thumb: if the problem has a recursive structure (trees, nested data), use recursion. If it’s a simple loop, use iteration.

Tail Recursion

When the recursive call is the very last operation, it’s “tail recursive.” Some languages (Scheme, Haskell) optimize this to use O(1) stack space. Python and Java do not.