Data Structure 📂 Part I – Foundations · 4 of 4 33 min read

Hands-On Recurrences

A practical, CLRS-aligned walkthrough of the Master Theorem for solving divide-and-conquer recurrences of the form T(n) = aT(n/b) + f(n). It builds intuition from the recursion tree, lays out all three cases (including the regularity condition for Case 3), works through Merge Sort, the classic 9T(n/3)+n and 3T(n/4)+n log n examples, Strassen and Karatsuba, ships a Python solver plus a level-cost simulator, and explains exactly when the theorem fails (gap cases, subtractive recurrences).

Section 01

The Story That Explains the Master Theorem

The Recursive Pizza Franchise
You run a pizza empire. To fulfil one giant catering order of size n, you don't cook it yourself — you split it among a regional managers, and hand each one an order 1/b the size. They split their orders the same way, and so on, until each slice is so tiny a single cook handles it.

At every level of this hierarchy somebody still has to do paperwork — divide the order, then later combine the boxes. That paperwork cost is f(n).

Here is the only question that ever matters for the total bill: where does most of the money go? Is it eaten up by the army of tiny cooks at the bottom, or by the paperwork at the top, or is it spread evenly across every level? Answer that, and you've solved the recurrence. That single question is the Master Theorem.

Most divide-and-conquer algorithms — merge sort, binary search, Karatsuba multiplication, Strassen's matrix product — describe their running time with a recurrence of the same shape. The Master Theorem (Chapter 4 of Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms) is a recipe that reads that shape and hands you a tight Θ-bound without any algebra.

🧩
The Core Insight

Compare two quantities: the work piled up at the leaves of the recursion tree, captured by nlogba, against the per-call divide-and-combine work f(n). Whichever grows faster decides the answer. They tie? Then every level costs the same and you pay a logarithmic number of times.


Section 02

Anatomy of a Recurrence

The Master Theorem applies to recurrences of exactly this form, where a ≥ 1 and b > 1 are constants and f(n) is asymptotically positive:

📐 The Standard Divide-and-Conquer Recurrence
T(n) = a · T(n/ b ) + f(n) how many shrink factor divide + combine
🎯
a — the branching factor
a ≥ 1
How many subproblems each call spawns. Merge sort makes 2 recursive calls, so a = 2. Binary search makes only 1, so a = 1.
✄️
b — the shrink factor
b > 1
Each subproblem has size n/b. Halving the input means b = 2. It controls how tall the recursion tree is: height ≈ logbn.
🧾
f(n) — the local work
asymptotically > 0
The non-recursive cost of one call: splitting the input and merging results. In merge sort the merge step is linear, so f(n) = Θ(n).
🔑
The Watershed Function

The whole theorem hinges on one derived quantity: nlogba. CLRS calls this the watershed function — it measures the total work done by all the leaves. Computing logba is always your first move.


Section 03

Three Ways to Solve a Recurrence

CLRS gives three tools. The Master Theorem is the fastest, but it grew out of the recursion-tree picture — understanding that picture is what makes the theorem stop feeling like memorisation.

✏️
Substitution
Guess the answer, then prove it by induction. Rigorous and general, but you need a good guess to start — often borrowed from the recursion tree.
guess & verify
🌲
Recursion Tree
Draw the tree, sum the cost at every level, add the levels up. Brilliant for intuition and for generating a guess — this is where the Master Theorem comes from.
sum the levels
Master Method
Match your recurrence to one of three cases and read off the answer. Zero algebra — but only works for the clean aT(n/b)+f(n) shape.
match a case

Section 04

The Recursion Tree — Where the Theorem Comes From

Picture merge sort: T(n) = 2T(n/2) + cn. Each node does cn work to merge, then splits into two half-sized children. Watch what the cost does at each level. The animated dot below traces the running total as we sweep down the tree.

🌳 Recursion Tree for T(n) = 2T(n/2) + cn
cn cn/2 cn/2 cn/4 cn/4 cn/4 cn/4 • • • (continues down to the leaves) • • • n leaves, each cost Θ(1) LEVEL COST level 0: 1 × cn = cn level 1: 2 × cn/2 = cn level 2: 4 × cn/4 = cn leaves: n × Θ(1) = cn Σ = cn × (lg n + 1) = Θ(n lg n)

Every level costs exactly cn. There are lg n + 1 levels. Multiply: Θ(n lg n). This perfectly balanced tree is the signature of Case 2.

📐
Why nlogba Counts the Leaves

The tree has height logbn, and each node has a children, so the bottom level holds alogbn = nlogba leaves. That algebraic identity is the heart of the watershed function — it's literally a leaf count.


Section 05

The Master Theorem — The Three Cases

Let p = logba. Compare f(n) with the watershed np. Exactly one of three things is true, and each gives a tight bound.

Case 1 — leaves win
if f(n) = O(np−ε) for some ε > 0
f(n) is polynomially smaller than the watershed. The cost piles up at the bottom of the tree, so the leaves dominate:  T(n) = Θ(np)
Case 2 — perfect tie
if f(n) = Θ(np · logkn), k ≥ 0
f(n) matches the watershed. Every level costs roughly the same, and there are Θ(log n) of them, so you pay one extra log factor:  T(n) = Θ(np · logk+1n)  (the classic form uses k = 0).
Case 3 — root wins
if f(n) = Ω(np+ε) AND a·f(n/b) ≤ c·f(n), c < 1
f(n) is polynomially larger than the watershed and satisfies the regularity condition. The work at the top dominates:  T(n) = Θ(f(n))

The same three cases, viewed as a contest between two stacks of work:

⚖️ Who Pays the Bill? — Leaves (np) vs Combine (f(n))
nⁿ f(n) Case 1 Θ(nⁿ) nⁿ f(n) Case 2 Θ(nⁿ log n) nⁿ f(n) Case 3 Θ(f(n)) p = logₛa

The glowing bar is the winner that sets the asymptotic cost. In Case 2 the bars match, so the log factor breaks the tie.


Section 06

A 4-Step Recipe for Applying the Theorem

01
Read off a, b, and f(n)
Identify the branching factor a, the shrink factor b, and the divide-and-combine cost f(n) directly from the recurrence.
02
Compute the watershed p = logba
This gives you np, the leaf-work yardstick. Use logba = ln a / ln b if it isn't a clean integer.
03
Compare f(n) against np
Is f(n) polynomially smaller (Case 1), the same up to a log power (Case 2), or polynomially larger (Case 3)? "Polynomially" means differing by a factor of nε.
04
Apply the case — and check regularity for Case 3
Read off the bound. Only Case 3 carries the extra obligation: verify a·f(n/b) ≤ c·f(n) for some c < 1 before you trust it.

Section 07

Worked Example 1 — Merge Sort (Case 2)

🧮 T(n) = 2T(n/2) + Θ(n)
Step 1
Read off: a = 2, b = 2, f(n) = Θ(n).
Step 2
Watershed: p = log₂2 = 1, so np = n1 = n.
Step 3
Compare: f(n) = n and np = n — they match exactly. That's Case 2 with k = 0.
Result
T(n) = Θ(n log n) — the running time of merge sort.

Section 08

Worked Example 2 — T(n) = 9T(n/3) + n (Case 1)

This is the textbook Case-1 example from CLRS.

🧮 T(n) = 9T(n/3) + n
Step 1
a = 9, b = 3, f(n) = n.
Step 2
Watershed: p = log₃9 = 2, so np = n2.
Step 3
Compare: f(n) = n = O(n2−ε) with ε = 1. f(n) is polynomially smaller → Case 1.
Result
T(n) = Θ(n2) — the leaves dominate.

Section 09

Worked Example 3 — T(n) = 3T(n/4) + n log n (Case 3)

Case 3 is the only one with a second hoop: the regularity condition. Here is the full check, also straight from CLRS.

🧮 T(n) = 3T(n/4) + n log n
Step 1
a = 3, b = 4, f(n) = n log n.
Step 2
Watershed: p = log₄3 ≈ 0.793, so np ≈ n0.793.
Step 3
Compare: f(n) = n log n = Ω(n0.793 + ε) — polynomially larger. Provisionally Case 3.
Step 4
Regularity: a·f(n/b) = 3·(n/4)log(n/4) = (3/4)·n·log(n/4) ≤ (3/4)·n log n = c·f(n) with c = 3/4 < 1. ✓ Holds.
Result
T(n) = Θ(n log n) — the root work dominates.
⚠️
Never Skip the Regularity Check

It is tempting to declare Case 3 the moment f(n) looks bigger. But the bound Θ(f(n)) only holds if the work shrinks geometrically as you go down the tree — that's exactly what a·f(n/b) ≤ c·f(n) guarantees. For the polynomial and polynomial-times-log f(n) you meet in practice, it almost always holds; pathological f(n) can fail it.


Section 10

A Cheat Sheet of Common Recurrences

Algorithm / Recurrenceabnlogbaf(n)CaseT(n)
Binary search — T(n/2)+Θ(1)12n0=112Θ(log n)
Tree traversal — 2T(n/2)+Θ(1)22n11Θ(n)
Merge sort — 2T(n/2)+Θ(n)22nn2Θ(n log n)
Karatsuba — 3T(n/2)+Θ(n)32n1.585n1Θ(n1.585)
CLRS Case 1 — 9T(n/3)+n93n2n1Θ(n2)
Strassen — 7T(n/2)+Θ(n2)72n2.807n21Θ(n2.807)
4T(n/2)+n242n2n22Θ(n2 log n)
CLRS Case 3 — 3T(n/4)+n log n34n0.793n log n3Θ(n log n)

Section 11

Python — A Master Theorem Solver

Here is a compact solver that classifies the case and reports the Θ-bound for any recurrence whose f(n) has the form ne·(log n)k.

from math import log, isclose

def master_theorem(a, b, f_exp, f_log=0):
    """Solve T(n) = a*T(n/b) + f(n), where f(n) = n^f_exp * (log n)^f_log."""
    assert a >= 1 and b > 1, "Master Theorem needs a >= 1 and b > 1"

    crit = log(a) / log(b)              # watershed exponent p = log_b(a)

    if f_exp < crit:                       # f(n) smaller -> leaves dominate
        return f"Case 1  ->  Theta(n^{crit:.4g})"

    elif isclose(f_exp, crit):             # tie -> one extra log factor
        k = f_log + 1
        return f"Case 2  ->  Theta(n^{crit:.4g} * log^{k} n)"

    else:                                  # f(n) larger -> root dominates
        return f"Case 3  ->  Theta(f(n))   [verify regularity!]"


# (name, a, b, exponent of n in f(n), power of log in f(n))
tests = [
    ("Merge Sort",    2, 2, 1, 0),
    ("Binary Search", 1, 2, 0, 0),
    ("Case 1 (CLRS)", 9, 3, 1, 0),
    ("Strassen",      7, 2, 2, 0),
    ("Case 3 (CLRS)", 3, 4, 1, 1),
]

for name, a, b, fe, fl in tests:
    print(f"{name:14s}: {master_theorem(a, b, fe, fl)}")
OUTPUT
Merge Sort : Case 2 -> Theta(n^1 * log^1 n) Binary Search : Case 2 -> Theta(n^0 * log^1 n) Case 1 (CLRS) : Case 1 -> Theta(n^2) Strassen : Case 1 -> Theta(n^2.807) Case 3 (CLRS) : Case 3 -> Theta(f(n)) [verify regularity!]

Proving Case 2 by Summing the Tree

This second snippet makes the recursion-tree picture concrete: it prints the work at every level for merge sort. Watch how every line lands on the same number — that's Case 2.

from math import log

def level_costs(a, b, n, f):
    """Print the work done at each level of the recursion tree."""
    depth = int(log(n, b))              # tree height = log_b(n)
    total = 0
    for i in range(depth + 1):
        nodes = a ** i                    # a^i nodes at level i
        size  = n / (b ** i)              # each subproblem has size n / b^i
        cost  = nodes * f(size)           # work at this level
        total += cost
        print(f"  level {i:2d}: {nodes:>6} nodes x f(n/{b**i:<4}) = {cost:,.0f}")
    print(f"  TOTAL work across the tree ~ {total:,.0f}")
    return total

# Merge sort: T(n) = 2T(n/2) + n, with n = 1024
level_costs(2, 2, 1024, lambda x: x)
OUTPUT
level 0: 1 nodes x f(n/1 ) = 1,024 level 1: 2 nodes x f(n/2 ) = 1,024 level 2: 4 nodes x f(n/4 ) = 1,024 level 3: 8 nodes x f(n/8 ) = 1,024 level 4: 16 nodes x f(n/16 ) = 1,024 level 5: 32 nodes x f(n/32 ) = 1,024 level 6: 64 nodes x f(n/64 ) = 1,024 level 7: 128 nodes x f(n/128 ) = 1,024 level 8: 256 nodes x f(n/256 ) = 1,024 level 9: 512 nodes x f(n/512 ) = 1,024 level 10: 1024 nodes x f(n/1024) = 1,024 TOTAL work across the tree ~ 11,264
🎯
11,264 = 1,024 × 11

Eleven identical levels of 1,024 each. That's n × (lg n + 1) with n = 1024 and lg 1024 = 10. The flat profile is the experimental fingerprint of Case 2, exactly as the animated tree predicted.


Section 12

When the Master Theorem Fails

The theorem is powerful but not universal. CLRS is explicit that gaps exist between the cases, and that some recurrences don't fit the form at all.

The gap below Case 3
f(n) bigger, but not polynomially
T(n) = 2T(n/2) + n log n. Here p = 1 and f(n) = n log n is larger than n, but only by a log factor — not by nε. No case applies. (The recursion tree gives Θ(n log2n).)
Regularity fails
Case 3 looks right, but isn't
If f(n) is larger but a·f(n/b) ≤ c·f(n) cannot be satisfied for any c < 1, Case 3's conclusion is not justified and the theorem stays silent.
Wrong shape entirely
subtractive / uneven splits
T(n) = T(n−1) + n (worst-case quicksort) shrinks by subtraction, not division. The Master Theorem doesn't apply — reach for the recursion tree or substitution instead.

Section 13

The Three Methods Compared

MethodHow it worksBest forLimitation
Substitution Guess a bound, prove it by induction Confirming a bound rigorously Needs a good initial guess
Recursion tree Draw the tree, sum cost per level, add levels Intuition + generating a guess Informal; arithmetic slips are easy
Master method Match to one of three cases, read the answer Clean aT(n/b)+f(n) forms Fails on gaps, uneven / subtractive splits

Section 14

Golden Rules

🌲 Master Theorem — Non-Negotiable Rules
1
Always compute p = logba first. The watershed np is your only yardstick — you cannot pick a case without it.
2
"Larger" and "smaller" mean polynomially. f(n) must differ from np by a factor of nε for some ε > 0. A mere log factor is not enough — that's the gap where the theorem fails.
3
Case 3 always requires the regularity check (a·f(n/b) ≤ c·f(n), c < 1). Cases 1 and 2 never do. Don't conflate them.
4
Ignore floors and ceilings. CLRS proves that replacing n/b with ⌊n/b⌋ or ⌈n/b⌉ does not change the asymptotic result, so drop them while solving.
5
The Master Theorem only covers T(n) = aT(n/b)+f(n). Subtractive recurrences like T(n−1)+f(n) or uneven splits need the recursion tree or substitution method.
6
When stuck, draw the tree. Every case of the Master Theorem is just a shortcut for summing level costs. The tree never lies, even where the theorem is silent.
7
Match the answer to the picture. Case 1 = bottom-heavy tree, Case 2 = flat profile, Case 3 = top-heavy tree. If your bound disagrees with the tree's shape, recheck your work.
You have completed Part I – Foundations. View all sections →