Fibonacci (Memoized)
Compute the n-th Fibonacci number again, but fix the exponential blow-up of the naive recursion by caching results. Given a non-negative integer n, return fib(n) with fib(0) = 0 and fib(1) = 1, using memoization so each distinct subproblem is computed at most once. Return the single integer.
- 0 <= n <= 40 (now fast enough that larger n is fine)
- fib(0) = 0, fib(1) = 1
- Use O(n) extra space for the cache
n = 00n = 68n = 1055n = 40102334155The naive Fibonacci recursion is correct but exponential, because the two branches recompute the same subproblems over and over — fib(3) was calculated again and again across the tree, each time from scratch. The recursion was forgetting. Memoization is the fix: remember each answer the first time you compute it, and return the stored value on every later request. It is the smallest possible change to the code and it turns O(2ⁿ) into O(n).
Keep a lookup table — call it memo — mapping n to fib(n). Before doing any work for a given n, check the memo. If the answer is already there, return it instantly and do not recurse. Otherwise compute it the normal way, store it, then return it. This is the exact same "spend memory to avoid repeated work" trade you saw in Two Sum, now applied to recursive calls instead of array scans.
def fib(n, memo={}):
if n <= 1: # base cases unchanged
return n
if n in memo: # already solved? return the cached value
return memo[n]
memo[n] = fib(n - 1, memo) + fib(n - 2, memo) # solve once...
return memo[n] # ...and remember itThe first time each value fib(k) is requested, it recurses and gets stored. Every subsequent request for fib(k) hits the memo and returns immediately, pruning that entire branch of the call tree before it can grow. So instead of an exponential tree, the recursion computes each of fib(0), fib(1), …, fib(n) exactly once — n + 1 distinct subproblems, each costing constant work on top of two cache lookups. That is O(n) time. The price is O(n) space for the memo (plus O(n) call-stack depth on the first descent).
memo={}. A default {} is created once and shared across all calls to fib, so it persists between separate top-level invocations — sometimes handy, often a subtle bug. The shape shown keeps the example short, but the professional habit is an explicit cache.Memoized recursion (also called top-down dynamic programming) is a two-line upgrade to any recursion with overlapping subproblems: (a) check the cache before computing, (b) fill the cache after computing. The slots to identify in a new problem: what uniquely identifies a subproblem (here, just n — so the key is n), and what the base cases are. Recognition signal: a naive recursion whose call tree revisits the same inputs (Fibonacci, climbing stairs, coin change, grid paths). When the subproblem key is a single integer marching in one direction, you can often flip this into a bottom-up loop filling an array — but the memoized recursion is the most direct expression, and it is the true bridge from plain recursion into dynamic programming.