Recursion
16 min
A recursive function calls itself on a smaller version of the problem, until it hits a base case simple enough to answer directly. Every recursive function needs both pieces:
def factorial(n):
if n == 0: # base case — stops the recursion
return 1
return n * factorial(n - 1) # recursive case — smaller subproblem
Each call to factorial waits on a "call stack" for the call below it to
return — that's the same stack data structure from the previous course.
Forget the base case, and you recurse forever until Python raises a
RecursionError.
Correct, but exponentially slow — fib(30) alone makes over a million calls, because fib(28) gets recomputed from scratch many times over.
Naive recursive Fibonacci is O(2ⁿ) because it re-solves the same subproblems repeatedly. Memoization — caching results you've already computed — turns it into O(n):
def fib_memo(n, memo=None):
if memo is None:
memo = {}
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo)
return memo[n]
This is your first taste of dynamic programming: recursion plus a cache of subproblem results, so no subproblem is ever solved twice.
Write a function `factorial(n)` that computes n! recursively (n * (n-1) * ... * 1, with `factorial(0) == 1`).
Write a function `fib_memo(n, memo=None)` that returns the nth Fibonacci number (0-indexed: fib(0)=0, fib(1)=1) using recursion WITH memoization, so repeated calls don't redo work.
What happens if a recursive function is missing a proper base case?
You can write a recursive function with a correct base case, and add memoization to avoid redundant recursive work.