Fibonacci
The Fibonacci sequence starts 0, 1, and every later term is the sum of the two before it: 0, 1, 1, 2, 3, 5, 8, 13, … Given a non-negative integer n, return the n-th Fibonacci number, where fib(0) = 0 and fib(1) = 1. Return the single integer. Write it as a direct recursion so the branching call tree is visible — then see why that tree is a problem.
- 0 <= n <= 30 (naive recursion becomes slow well before this)
- fib(0) = 0, fib(1) = 1
- The result fits comfortably in a 32-bit integer for n ≤ 30
n = 00n = 11n = 55n = 1055Fibonacci is defined by its own two predecessors: fib(n) = fib(n − 1) + fib(n − 2), starting from fib(0) = 0 and fib(1) = 1. The definition is recursive on its face, so the code almost writes itself — and that is exactly the trap. This problem introduces binary recursion: a function that calls itself twice, turning the straight chain from Factorial into a branching tree.
There are two base cases this time (n = 0 and n = 1), because the recurrence reaches back two steps and needs two anchored starting values. Then the recursive case adds the two smaller answers:
def fib(n):
if n <= 1: # two base cases folded into one test: fib(0)=0, fib(1)=1
return n
return fib(n - 1) + fib(n - 2) # TWO recursive calls — this branchesThe leap of faith is the same as before: trust that fib(n − 1) and fib(n − 2) each return the right value, and just add them. It is correct. The catastrophe is in what it costs.
With one recursive call (Factorial), the calls form a line. With two, each call spawns two more, which spawn two more — a binary tree of calls that roughly doubles in size at every level. Worse, the two branches overlap and recompute the same values independently. Watch fib(5) start to expand:
fib(5) = fib(4) + fib(3)
fib(4) = fib(3) + fib(2) <- fib(3) is now being computed twice
fib(3) = fib(2) + fib(1) <- fib(2) computed again... and again...fib(3) is computed 2 times, fib(2) is computed 3 times, fib(1) even more — and none of them remember each other's work. The number of calls grows like the Fibonacci numbers themselves, which is roughly O(2ⁿ) — exponential. fib(30) already triggers over a million calls; fib(50) would take longer than you will wait. The recursion is correct but does an astronomical amount of duplicated work.
Fibonacci is the canonical example of overlapping subproblems: the same smaller inputs are solved again and again across different branches. The recursion itself is not the villain — the forgetting is. The moment you notice a recursive function recomputing an input it has already solved, there is a fix: remember each answer the first time and look it up thereafter. That single idea collapses the exponential tree back into a linear walk, and it is important enough to be its own problem: Fibonacci (Memoized), next. Recognizing overlapping subproblems is also the exact doorway from recursion into dynamic programming — DP is, at heart, recursion plus memory.