Skip to content

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 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.

A divide-and-conquer algorithm that splits an input of size nn into aa subproblems of size n/bn/b, then spends f(n)f(n) extra work combining the results, has total cost:

T(n)=aT(n/b)+f(n)T(n) = a \, T(n/b) + f(n)
  • aa — how many subproblems you recurse into.
  • bb — how much smaller each subproblem is (nn shrinks to n/bn/b).
  • f(n)f(n) — the work done outside the recursive calls (splitting + combining).

Merge sort, for example, splits into 2 halves (a=2a = 2, b=2b = 2) and spends O(n)O(n) merging them back together — giving T(n)=2T(n/2)+O(n)T(n) = 2T(n/2) + O(n).

Draw the recursion as a tree: the root does f(n)f(n) work, its aa children each do f(n/b)f(n/b) work, their children do f(n/b2)f(n/b^2), and so on until subproblems hit the base case. Summing every level’s cost gives the total.

diagram Recursion tree for merge sort: T(n) = 2T(n/2) + O(n) mermaid

Every level’s costs add up to roughly nn (the halves sum back to the whole), and there are log2n\log_2 n levels before subproblems reach size 1. So total cost is nlog2n=O(nlogn)n \cdot \log_2 n = O(n \log n) — no need to draw the whole tree once you see the pattern.

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 logbn\log_b n in the theorem:

recursionT(n) = T(n-1) + T(n-2) + O(1), drawnthe tree IS the recurrence
f6f5f4f3f2f1f0f1f2f1f0f3f2f1f0f1f4f3f2f1f0f1f2f1f0
call stack
f6
n6calls so far1
callfib(6) needs fib(5) and fib(4). Neither is known, so both are computed from scratch — including everything they in turn need.
1/51

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.

Drawing a tree every time is tedious. The Master Theorem gives a direct answer for T(n)=aT(n/b)+f(n)T(n) = aT(n/b) + f(n) (with a1a \ge 1, b>1b > 1) by comparing f(n)f(n) to nlogban^{\log_b a} — the cost if there were no extra work at all:

Case 1: f(n)=O ⁣(nlogbaϵ) for some ϵ>0    T(n)=Θ ⁣(nlogba)Case 2: f(n)=Θ ⁣(nlogba)    T(n)=Θ ⁣(nlogbalogn)Case 3: f(n)=Ω ⁣(nlogba+ϵ) and af(n/b)cf(n) for some c<1    T(n)=Θ ⁣(f(n))\begin{aligned} \textbf{Case 1: } & f(n) = O\!\left(n^{\log_b a - \epsilon}\right) \text{ for some } \epsilon > 0 \;\Rightarrow\; T(n) = \Theta\!\left(n^{\log_b a}\right) \\[4pt] \textbf{Case 2: } & f(n) = \Theta\!\left(n^{\log_b a}\right) \;\Rightarrow\; T(n) = \Theta\!\left(n^{\log_b a} \log n\right) \\[4pt] \textbf{Case 3: } & f(n) = \Omega\!\left(n^{\log_b a + \epsilon}\right) \text{ and } a f(n/b) \le c f(n) \text{ for some } c < 1 \;\Rightarrow\; T(n) = \Theta\!\left(f(n)\right) \end{aligned}

In plain language: if the combine step is smaller than the recursive work, the recursion dominates (Case 1). If they’re equal, add a logn\log n factor (Case 2). If the combine step dominates, the top level alone sets the cost (Case 3).

diagram Master Theorem — which case applies? mermaid

Merge sort: T(n)=2T(n/2)+O(n)T(n) = 2T(n/2) + O(n). Here a=2a = 2, b=2b = 2, so logba=log22=1\log_b a = \log_2 2 = 1, and f(n)=O(n1)f(n) = O(n^1) — the same order. That’s Case 2, so:

T(n)=Θ(nlogn)T(n) = \Theta(n \log n)

Binary search: T(n)=T(n/2)+O(1)T(n) = T(n/2) + O(1). Here a=1a = 1, b=2b = 2, so logba=log21=0\log_b a = \log_2 1 = 0, and f(n)=O(n0)=O(1)f(n) = O(n^0) = O(1) — again the same order. Also Case 2, giving:

T(n)=Θ(logn)T(n) = \Theta(\log n)

Instead of drawing trees by hand, you can compute T(n)T(n) numerically by literally implementing the recurrence, and compare it to the closed-form prediction:

recurrence_simulator.py
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)

For T(n)=aT(n/b)+f(n)T(n) = a\,T(n/b) + f(n) with f(n)=ndf(n) = n^d, compare dd against logba\log_b a:

Recurrenceaabbddlogba\log_b aCaseResult
Merge sort: 2T(n/2)+n2T(n/2) + n22112 (equal)O(nlogn)O(n \log n)
Binary search: T(n/2)+1T(n/2) + 112002 (equal)O(logn)O(\log n)
4T(n/2)+n4T(n/2) + n42121 (leaves win)O(n2)O(n^2)
3T(n/2)+n3T(n/2) + n3211.5851 (leaves win)O(n1.585)O(n^{1.585})
Strassen: 7T(n/2)+n27T(n/2) + n^27222.8071 (leaves win)O(n2.807)O(n^{2.807})
2T(n/2)+n22T(n/2) + n^222213 (root wins)O(n2)O(n^2)

All six computed rather than recalled. Three things the table makes concrete:

  • logba\log_b a is “how fast the subproblems multiply”. It is the exponent of the leaf count: there are alogbn=nlogbaa^{\log_b n} = n^{\log_b a} leaves. Comparing it against dd 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 logn\log n factor, and it happens exactly when the two exponents tie — every level does the same total work, and there are logbn\log_b n levels. Merge sort and binary search are both case 2, which is why both have a bare log\log in them.
  • log23=1.585\log_2 3 = 1.585 is not a typo. 3T(n/2)+n3T(n/2) + n really is O(n1.585)O(n^{1.585}) — Karatsuba multiplication — and it is faster than n2n^2 precisely because 3 subproblems is fewer than 4. Same for Strassen: log27=2.807<3\log_2 7 = 2.807 < 3, which is the entire point of the algorithm.

For merge sort at n = 8, the work per level:

LevelSubproblem sizeCountWork per level
0818
1428
2248
3188

log28+1=4\log_2 8 + 1 = 4 levels, 8 units each — so nlognn \log n. 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, O(n2)O(n^2). That is case 1. Change the merge cost to n2n^2 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.

Drill 1 — the critical exponent. logba\log_b a 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 f(n)f(n)‘s exponent, decide which case applies.

RecurrenceSolutionWhere it comes from
T(n)=T(n/2)+O(1)T(n) = T(n/2) + O(1)O(logn)O(\log n)binary search
T(n)=T(n/2)+O(n)T(n) = T(n/2) + O(n)O(n)O(n)quickselect (expected), root dominates
T(n)=2T(n/2)+O(1)T(n) = 2T(n/2) + O(1)O(n)O(n)tree traversal — leaf-dominated
T(n)=2T(n/2)+O(n)T(n) = 2T(n/2) + O(n)O(nlogn)O(n \log n)merge sort, balanced quicksort
T(n)=2T(n/2)+O(n2)T(n) = 2T(n/2) + O(n^2)O(n2)O(n^2)root dominates
T(n)=T(n1)+O(1)T(n) = T(n-1) + O(1)O(n)O(n)linear recursion — not a Master Theorem shape
T(n)=T(n1)+O(n)T(n) = T(n-1) + O(n)O(n2)O(n^2)selection sort, insertion sort
T(n)=2T(n1)+O(1)T(n) = 2T(n-1) + O(1)O(2n)O(2^n)subsets, Towers of Hanoi
T(n)=T(n1)+T(n2)T(n) = T(n-1) + T(n-2)O(ϕn)O(\phi^n)naive Fibonacci — ϕ1.618\phi \approx 1.618, not 2n2^n
T(n)=nT(n1)T(n) = nT(n-1)O(n!)O(n!)permutations

The theorem does not cover everything, and knowing the boundary matters:

  • Subtract-and-conquer (T(n1)T(n-1), not T(n/b)T(n/b)) 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. T(n)=T(n/3)+T(2n/3)+nT(n) = T(n/3) + T(2n/3) + n is not of the form, though the recursion-tree argument still gives O(nlogn)O(n \log n) — the depth is just log3/2n\log_{3/2} n instead.
  • Case 3 needs a regularity condition (af(n/b)cf(n)a f(n/b) \le c f(n) for some c<1c < 1), which every polynomial ff satisfies. It matters only for exotic ff, so in practice “root dominates” is safe.
  • Naive Fibonacci is Θ(ϕn)\Theta(\phi^n), not Θ(2n)\Theta(2^n). Measured call counts divided by ϕn\phi^n are a constant 1.45 at n=10,20,25,30n = 10, 20, 25, 30 — while 2n2^n overestimates by 12x at n=30n = 30. 2n2^n is a valid upper bound and a wrong tight bound.
  • Using the theorem on T(n1)T(n-1) recurrences. It only applies to T(n/b)T(n/b) — divide, not subtract. T(n)=T(n1)+nT(n) = T(n-1) + n is O(n2)O(n^2) by unrolling, and no case of the theorem describes it.
  • Comparing f(n)f(n) against nn instead of against nlogban^{\log_b a}. The comparison is always against the leaf-count exponent. For 4T(n/2)+n4T(n/2) + n, f=nf = n looks “linear and therefore cheap” but log24=2\log_2 4 = 2, so the leaves dominate and the answer is O(n2)O(n^2).
  • Forgetting that logba\log_b a is usually not an integer. 3T(n/2)+n3T(n/2) + n gives O(n1.585)O(n^{1.585}) — Karatsuba. Rounding to n2n^2 throws away the entire reason the algorithm exists.
  • Quoting naive Fibonacci as O(2n)O(2^n). It is Θ(ϕn)\Theta(\phi^n). Measured: 2,692,537 calls at n=30n = 30 against 2301.072^{30} \approx 1.07 billion — a 400x overestimate. Correct as an upper bound, wrong as a tight one.
  • Assuming case 2 whenever you see a log\log. Case 2 requires the exponents to be equal. A log\log in f(n)f(n) itself (say f(n)=nlognf(n) = n \log n) 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. nn leaves each doing O(n)O(n) is O(n2)O(n^2) 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.
They askWhat they’re checkingThe answer
“Solve T(n)=2T(n/2)+nT(n) = 2T(n/2) + nWhether you can do it two waysO(nlogn)O(n \log n). Either case 2 (log22=1=d\log_2 2 = 1 = d), or the tree: logn\log n levels of nn work each. The tree argument is the one to give, because it generalises
“Why does merge sort have a log\log but tree traversal does not?”Understanding, not recallTraversal is 2T(n/2)+O(1)2T(n/2) + O(1) — the combine is free, so the leaves dominate and it is O(n)O(n). Merge sort’s combine is O(n)O(n), which exactly ties the leaf growth: case 2, hence the log\log
“What is logba\log_b a intuitively?”DepthThe exponent of the leaf count: there are nlogban^{\log_b a} leaves. Comparing it against ff‘s exponent asks whether the work sits at the leaves, is spread evenly, or sits at the root
“Solve T(n)=T(n1)+nT(n) = T(n-1) + nWhether you notice it is out of scopeNot a Master Theorem shape — it subtracts rather than divides. Unroll: n+(n1)+=O(n2)n + (n-1) + \cdots = O(n^2). This is insertion and selection sort
“Naive Fibonacci’s complexity?”PrecisionΘ(ϕn)\Theta(\phi^n), ϕ1.618\phi \approx 1.618not Θ(2n)\Theta(2^n). Measured call counts are 1.45 ϕn\phi^n at every nn; 2n2^n overestimates 400x at n=30n = 30. It is a correct upper bound and a wrong tight bound
“Strassen is 7T(n/2)+n27T(n/2) + n^2. Why is that an improvement?”Applying itlog27=2.807<3\log_2 7 = 2.807 < 3, so O(n2.807)O(n^{2.807}) beats the O(n3)O(n^3) of naive matrix multiplication. Seven subproblems instead of eight is the whole algorithm
T(n)=T(n/3)+T(2n/3)+nT(n) = T(n/3) + T(2n/3) + n?”The limitsUnequal splits are outside the theorem, but the tree still works: every level totals nn, and the depth is log3/2n\log_{3/2} n — so O(nlogn)O(n \log n), with a worse constant than a balanced split
“Do you need to memorise the three cases?”JudgementNo — 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
pch.quizTag pch.quizDefaultTitle
  1. What is log_b(a) intuitively, in a divide-and-conquer recurrence?

    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".

  2. T(n) = 3T(n/2) + n. What is the solution?

    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.

  3. Why do merge sort (2T(n/2) + n) and tree traversal (2T(n/2) + O(1)) have different bounds?

    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.

  4. Can the Master Theorem solve T(n) = T(n-1) + n?

    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.

  5. Naive recursive Fibonacci makes 2,692,537 calls at n = 30. Is it O(2^n)?

    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.

  6. When does the Master Theorem produce a log n factor?

    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.

  7. You cannot recall which case is which. What is the reliable fallback?

    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.

  • T(n)=aT(n/b)+ndT(n) = a\,T(n/b) + n^d: compare dd against logba\log_b a. Less -> leaves win, O(nlogba)O(n^{\log_b a}). Equal -> case 2, O(nlogbalogn)O(n^{\log_b a} \log n). Greater -> root wins, O(nd)O(n^d).
  • logba\log_b a is the leaf-count exponent — there are nlogban^{\log_b a} leaves. That is the whole intuition.
  • Only case 2 produces a bare logn\log n, because that is the one where every level ties and the level count survives.
  • Merge sort 2T(n/2)+n2T(n/2)+n -> O(nlogn)O(n\log n) (case 2); tree traversal 2T(n/2)+O(1)2T(n/2)+O(1) -> O(n)O(n) (case 1). Same split, different combine.
  • Non-integer exponents are real: 3T(n/2)+n3T(n/2)+n is O(n1.585)O(n^{1.585}) (Karatsuba), Strassen’s 7T(n/2)+n27T(n/2)+n^2 is O(n2.807)O(n^{2.807}) — both beat the naive bound because they cut the branching factor.
  • The theorem does not cover T(n1)T(n-1) — subtract-and-conquer is unrolled instead: T(n1)+nO(n2)T(n-1)+n \Rightarrow O(n^2), 2T(n1)+1O(2n)2T(n-1)+1 \Rightarrow O(2^n).
  • Nor unequal splits — but the tree still works: T(n/3)+T(2n/3)+nT(n/3)+T(2n/3)+n is O(nlogn)O(n\log n) with depth log3/2n\log_{3/2} n.
  • Naive Fibonacci is Θ(ϕn)\Theta(\phi^n), not 2n2^n — measured 1.45ϕn\phi^n at every nn; 2302^{30} overestimates 400x.
  • When in doubt, draw two levels and ask: growing, flat, or shrinking?
  • Divide-and-conquer cost is captured by T(n)=aT(n/b)+f(n)T(n) = aT(n/b) + f(n).
  • The recursion tree method sums cost per level; number of levels is logbn\log_b n.
  • The Master Theorem gives the closed form directly by comparing f(n)f(n) to nlogban^{\log_b a} — no tree required.
  • Merge sort: Θ(nlogn)\Theta(n \log n). Binary search: Θ(logn)\Theta(\log n). Both land in Case 2.

Next: Space Complexity and the Call Stack — what recursion actually costs in memory, not just time.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading