Recurrences and the Master Theorem
Merge sort, binary search, and most “divide and conquer” algorithms all share one shape: split the problem, solve the pieces recursively, combine the results. A recurrence relation captures that shape as an equation, and solving it tells you the Big-O without simulating a single run.
What you’ll learn
Section titled “What you’ll learn”- What a recurrence relation is and where it comes from.
- The recursion tree method — solving a recurrence by hand, level by level.
- The Master Theorem and its three cases.
- How merge sort and binary search reduce to closed-form Big-O.
- A small simulator that computes recurrence cost numerically.
Where a recurrence comes from
Section titled “Where a recurrence comes from”A divide-and-conquer algorithm that splits an input of size into subproblems of size , then spends extra work combining the results, has total cost:
- — how many subproblems you recurse into.
- — how much smaller each subproblem is ( shrinks to ).
- — the work done outside the recursive calls (splitting + combining).
Merge sort, for example, splits into 2 halves (, ) and spends merging them back together — giving .
The recursion tree method
Section titled “The recursion tree method”Draw the recursion as a tree: the root does work, its children each do work, their children do , and so on until subproblems hit the base case. Summing every level’s cost gives the total.
graph TD
A["T(n)
merge cost: n"] --> B["T(n/2)
merge cost: n/2"]
A --> C["T(n/2)
merge cost: n/2"]
B --> D["T(n/4)
cost: n/4"]
B --> E["T(n/4)
cost: n/4"]
C --> F["T(n/4)
cost: n/4"]
C --> G["T(n/4)
cost: n/4"]
Every level’s costs add up to roughly (the halves sum back to the whole), and there are levels before subproblems reach size 1. So total cost is — no need to draw the whole tree once you see the pattern.
Visual intuition
Section titled “Visual intuition”A recurrence is a recursion tree, and the tree is where the cost comes from. Watch the branching
factor and the depth — those two numbers are the a and the in the theorem:
Count nodes per level rather than in total: that is exactly what the recursion-tree method does. Here the branching factor is 2 and the depth is n, giving the exponential blow-up. Contrast a divide-and-conquer recurrence, where the subproblem size halves and the depth collapses to log n.
The Master Theorem
Section titled “The Master Theorem”Drawing a tree every time is tedious. The Master Theorem gives a direct answer for (with , ) by comparing to — the cost if there were no extra work at all:
In plain language: if the combine step is smaller than the recursive work, the recursion dominates (Case 1). If they’re equal, add a factor (Case 2). If the combine step dominates, the top level alone sets the cost (Case 3).
graph TD
A["Compare f(n) to n^(log_b a)"] --> B{"Is f(n) polynomially smaller?"}
B -- Yes --> C["Case 1
T(n) = Theta(n^(log_b a))"]
B -- No --> D{"Is f(n) equal (same order)?"}
D -- Yes --> E["Case 2
T(n) = Theta(n^(log_b a) * log n)"]
D -- No --> F{"Is f(n) polynomially larger
+ regularity condition holds?"}
F -- Yes --> G["Case 3
T(n) = Theta(f(n))"]
Worked examples
Section titled “Worked examples”Merge sort: . Here , , so , and — the same order. That’s Case 2, so:
Binary search: . Here , , so , and — again the same order. Also Case 2, giving:
A recurrence-cost simulator
Section titled “A recurrence-cost simulator”Instead of drawing trees by hand, you can compute numerically by literally implementing the recurrence, and compare it to the closed-form prediction:
def recurrence_cost(n, a, b, f, base=1):
"""Numerically evaluate T(n) = a * T(n // b) + f(n)."""
if n <= base:
return 1
return a * recurrence_cost(n // b, a, b, f, base) + f(n)
n = 64
# Merge sort: T(n) = 2T(n/2) + n -> should track n * log2(n)
merge_cost = recurrence_cost(n, a=2, b=2, f=lambda n: n)
print("merge sort cost: ", merge_cost, " ~ n log n =", n * n.bit_length())
# Binary search: T(n) = T(n/2) + 1 -> should track log2(n)
search_cost = recurrence_cost(n, a=1, b=2, f=lambda n: 1)
print("binary search cost:", search_cost, " ~ log n =", n.bit_length() - 1)Dry run
Section titled “Dry run”Applying the theorem to six recurrences
Section titled “Applying the theorem to six recurrences”For with , compare against :
| Recurrence | Case | Result | ||||
|---|---|---|---|---|---|---|
| Merge sort: | 2 | 2 | 1 | 1 | 2 (equal) | |
| Binary search: | 1 | 2 | 0 | 0 | 2 (equal) | |
| 4 | 2 | 1 | 2 | 1 (leaves win) | ||
| 3 | 2 | 1 | 1.585 | 1 (leaves win) | ||
| Strassen: | 7 | 2 | 2 | 2.807 | 1 (leaves win) | |
| 2 | 2 | 2 | 1 | 3 (root wins) |
All six computed rather than recalled. Three things the table makes concrete:
- is “how fast the subproblems multiply”. It is the exponent of the leaf count: there are leaves. Comparing it against is literally asking “is the work concentrated at the leaves, spread evenly, or concentrated at the root?”
- Case 2 is the only one that produces a factor, and it happens exactly when the two exponents tie — every level does the same total work, and there are levels. Merge sort and binary search are both case 2, which is why both have a bare in them.
- is not a typo. really is — Karatsuba multiplication — and it is faster than precisely because 3 subproblems is fewer than 4. Same for Strassen: , which is the entire point of the algorithm.
Reading the recursion tree instead
Section titled “Reading the recursion tree instead”For merge sort at n = 8, the work per level:
| Level | Subproblem size | Count | Work per level |
|---|---|---|---|
| 0 | 8 | 1 | 8 |
| 1 | 4 | 2 | 8 |
| 2 | 2 | 4 | 8 |
| 3 | 1 | 8 | 8 |
levels, 8 units each — so . The pieces get smaller and there are proportionally more of them, which is what makes every level cost the same. That balance is case 2, and seeing it in the tree is more reliable than remembering which case is which.
Change the branching to 4 and the level totals become 8, 16, 32, 64 — geometrically increasing, so the last level dominates and the answer is the leaf count, . That is case 1. Change the merge cost to and they become 64, 32, 16, 8 — decreasing, so the root dominates: case 3. Three shapes, three cases, and the tree shows which one you are in.
Practice
Section titled “Practice”Drill 1 — the critical exponent. is the exponent every Master
Theorem case compares against. Compute it with math.log(a, b).
Drill 2 — simulate the recurrence. Finish the recursive call so the simulator actually recurses on the smaller subproblem.
Drill 3 — pick the Master Theorem case. Given the critical exponent and ‘s exponent, decide which case applies.
Complexity
Section titled “Complexity”| Recurrence | Solution | Where it comes from |
|---|---|---|
| binary search | ||
| quickselect (expected), root dominates | ||
| tree traversal — leaf-dominated | ||
| merge sort, balanced quicksort | ||
| root dominates | ||
| linear recursion — not a Master Theorem shape | ||
| selection sort, insertion sort | ||
| subsets, Towers of Hanoi | ||
| naive Fibonacci — , not | ||
| permutations |
The theorem does not cover everything, and knowing the boundary matters:
- Subtract-and-conquer (, not ) is out of scope. The Master Theorem is for dividing. Those rows above are solved by unrolling, and they are the ones that actually appear in interview recursion.
- Unequal splits are out of scope. is not of the form, though the recursion-tree argument still gives — the depth is just instead.
- Case 3 needs a regularity condition ( for some ), which every polynomial satisfies. It matters only for exotic , so in practice “root dominates” is safe.
- Naive Fibonacci is , not . Measured call counts divided by are a constant 1.45 at — while overestimates by 12x at . is a valid upper bound and a wrong tight bound.
Pitfalls
Section titled “Pitfalls”- Using the theorem on recurrences. It only applies to — divide, not subtract. is by unrolling, and no case of the theorem describes it.
- Comparing against instead of against . The comparison is always against the leaf-count exponent. For , looks “linear and therefore cheap” but , so the leaves dominate and the answer is .
- Forgetting that is usually not an integer. gives — Karatsuba. Rounding to throws away the entire reason the algorithm exists.
- Quoting naive Fibonacci as . It is . Measured: 2,692,537 calls at against billion — a 400x overestimate. Correct as an upper bound, wrong as a tight one.
- Assuming case 2 whenever you see a . Case 2 requires the exponents to be equal. A in itself (say ) needs the extended form of the theorem.
- Ignoring the base case’s cost. The theorem describes the recursion; if the base case does non-constant work, that multiplies the leaf count. leaves each doing is however cheap the combine step is.
- Treating the theorem as a substitute for the tree. Drawing two levels and asking “is the work growing, flat, or shrinking?” answers the question without recalling which case is which — and it works on the recurrences the theorem does not cover.
Interview follow-ups
Section titled “Interview follow-ups”| They ask | What they’re checking | The answer |
|---|---|---|
| “Solve ” | Whether you can do it two ways | . Either case 2 (), or the tree: levels of work each. The tree argument is the one to give, because it generalises |
| “Why does merge sort have a but tree traversal does not?” | Understanding, not recall | Traversal is — the combine is free, so the leaves dominate and it is . Merge sort’s combine is , which exactly ties the leaf growth: case 2, hence the |
| “What is intuitively?” | Depth | The exponent of the leaf count: there are leaves. Comparing it against ‘s exponent asks whether the work sits at the leaves, is spread evenly, or sits at the root |
| “Solve ” | Whether you notice it is out of scope | Not a Master Theorem shape — it subtracts rather than divides. Unroll: . This is insertion and selection sort |
| “Naive Fibonacci’s complexity?” | Precision | , — not . Measured call counts are 1.45 at every ; overestimates 400x at . It is a correct upper bound and a wrong tight bound |
| “Strassen is . Why is that an improvement?” | Applying it | , so beats the of naive matrix multiplication. Seven subproblems instead of eight is the whole algorithm |
| ”?” | The limits | Unequal splits are outside the theorem, but the tree still works: every level totals , and the depth is — so , with a worse constant than a balanced split |
| “Do you need to memorise the three cases?” | Judgement | No — draw two levels and see whether the per-level work grows, stays flat, or shrinks. Growing means leaf-dominated, flat means multiply by the depth, shrinking means root-dominated. The tree also covers what the theorem does not |
Self-check
Section titled “Self-check”-
What is log_b(a) intuitively, in a divide-and-conquer recurrence?
Depth is log_b(n), a different quantity. Each level multiplies the subproblem count by a and divides the size by b, so after log_b(n) levels there are a^(log_b n) = n^(log_b a) leaves. The three cases are just "leaves dominate", "every level ties", and "the root dominates".
pch.quizShowAnswer
B — The exponent of the LEAF COUNT -- there are n^(log_b a) leaves, so comparing it against f's exponent asks where the work sits — Depth is log_b(n), a different quantity. Each level multiplies the subproblem count by a and divides the size by b, so after log_b(n) levels there are a^(log_b n) = n^(log_b a) leaves. The three cases are just "leaves dominate", "every level ties", and "the root dominates".
-
T(n) = 3T(n/2) + n. What is the solution?
This is Karatsuba multiplication, and the non-integer exponent is the point: three subproblems rather than four is exactly what beats the O(n^2) schoolbook method. Rounding 1.585 up to 2 throws away the entire reason the algorithm exists.
pch.quizShowAnswer
B — O(n^1.585), since log2(3) = 1.585 > 1 -- the leaves dominate — This is Karatsuba multiplication, and the non-integer exponent is the point: three subproblems rather than four is exactly what beats the O(n^2) schoolbook method. Rounding 1.585 up to 2 throws away the entire reason the algorithm exists.
-
Why do merge sort (2T(n/2) + n) and tree traversal (2T(n/2) + O(1)) have different bounds?
Same a and b, different f, different case. For traversal, log2(2) = 1 > 0 = d, so case 1 gives O(n^1). For merge sort the exponents tie at 1, so case 2 multiplies by the depth. This pair is the cleanest illustration that the combine cost -- not the branching -- decides whether a log appears.
pch.quizShowAnswer
B — Traversal's combine is free, so the leaves dominate: O(n). Merge sort's O(n) combine exactly ties the leaf growth, giving case 2 and the log factor — Same a and b, different f, different case. For traversal, log2(2) = 1 > 0 = d, so case 1 gives O(n^1). For merge sort the exponents tie at 1, so case 2 multiplies by the depth. This pair is the cleanest illustration that the combine cost -- not the branching -- decides whether a log appears.
-
Can the Master Theorem solve T(n) = T(n-1) + n?
Subtract-and-conquer is out of scope entirely. Unrolling gives n + (n-1) + (n-2) + ... = n(n+1)/2 = O(n^2) -- which is insertion sort and selection sort. Knowing where the theorem stops applying matters, because these subtracting recurrences are the ones that actually turn up in interview recursion.
pch.quizShowAnswer
B — No -- the theorem is for DIVIDING (T(n/b)). This subtracts, and unrolling gives O(n^2) — Subtract-and-conquer is out of scope entirely. Unrolling gives n + (n-1) + (n-2) + ... = n(n+1)/2 = O(n^2) -- which is insertion sort and selection sort. Knowing where the theorem stops applying matters, because these subtracting recurrences are the ones that actually turn up in interview recursion.
-
Naive recursive Fibonacci makes 2,692,537 calls at n = 30. Is it O(2^n)?
Measured call counts divided by phi^n give a constant 1.45 at n = 10, 20, 25 and 30 -- the signature of Theta(phi^n). 2^30 is about 1.07 billion against the actual 2.7 million. The recurrence T(n) = T(n-1) + T(n-2) has the Fibonacci growth rate by definition, which is why phi appears.
pch.quizShowAnswer
B — It is a valid upper bound but not tight: the real growth is Theta(phi^n) with phi = 1.618, and 2^30 overestimates by about 400x — Measured call counts divided by phi^n give a constant 1.45 at n = 10, 20, 25 and 30 -- the signature of Theta(phi^n). 2^30 is about 1.07 billion against the actual 2.7 million. The recurrence T(n) = T(n-1) + T(n-2) has the Fibonacci growth rate by definition, which is why phi appears.
-
When does the Master Theorem produce a log n factor?
The log is the level count, and it only survives when the per-level work is flat -- geometric growth or decay is dominated by one end of the tree instead. That is why merge sort and binary search both carry a bare log and why 4T(n/2)+n, which is leaf-dominated, does not.
pch.quizShowAnswer
B — Only in case 2, when f's exponent equals log_b(a) -- every level does the same total work and there are log_b(n) levels — The log is the level count, and it only survives when the per-level work is flat -- geometric growth or decay is dominated by one end of the tree instead. That is why merge sort and binary search both carry a bare log and why 4T(n/2)+n, which is leaf-dominated, does not.
-
You cannot recall which case is which. What is the reliable fallback?
Growing means the leaves dominate; flat means multiply by the depth; shrinking means the root dominates. For merge sort at n = 8 the levels are 8, 8, 8, 8 -- flat, so n log n. Change the branching to 4 and they become 8, 16, 32, 64 -- growing, so O(n^2). The tree also handles unequal splits like T(n/3) + T(2n/3) + n, which the theorem cannot.
pch.quizShowAnswer
B — Draw two levels and ask whether the per-level total is growing, flat, or shrinking -- and it also handles recurrences the theorem does not cover — Growing means the leaves dominate; flat means multiply by the depth; shrinking means the root dominates. For merge sort at n = 8 the levels are 8, 8, 8, 8 -- flat, so n log n. Change the branching to 4 and they become 8, 16, 32, 64 -- growing, so O(n^2). The tree also handles unequal splits like T(n/3) + T(2n/3) + n, which the theorem cannot.
Recall card
Section titled “Recall card”- : compare against . Less -> leaves win, . Equal -> case 2, . Greater -> root wins, .
- is the leaf-count exponent — there are leaves. That is the whole intuition.
- Only case 2 produces a bare , because that is the one where every level ties and the level count survives.
- Merge sort -> (case 2); tree traversal -> (case 1). Same split, different combine.
- Non-integer exponents are real: is (Karatsuba), Strassen’s is — both beat the naive bound because they cut the branching factor.
- The theorem does not cover — subtract-and-conquer is unrolled instead: , .
- Nor unequal splits — but the tree still works: is with depth .
- Naive Fibonacci is , not — measured 1.45 at every ; overestimates 400x.
- When in doubt, draw two levels and ask: growing, flat, or shrinking?
- Divide-and-conquer cost is captured by .
- The recursion tree method sums cost per level; number of levels is .
- The Master Theorem gives the closed form directly by comparing to — no tree required.
- Merge sort: . Binary search: . Both land in Case 2.
Next: Space Complexity and the Call Stack — what recursion actually costs in memory, not just time.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading