Chapter 4: Complexity, Memory and Performance Analysis
4.04Beginner6 topics18 min4 interactive animations

Recurrence relations

A recurrence relation expresses the cost of a recursive algorithm in terms of smaller inputs. This lesson solves the two standard recurrences and teaches substitution, recursion trees, the Master theorem and an overview of Akra-Bazzi.

Learning goals

  • Write the recurrence for a recursive algorithm
  • Solve it with recursion trees and substitution
  • Apply the three cases of the Master theorem

Prerequisites

Loop complexitySummations and sequences
4.04.1

T(n)=T(n-1)+O(1)

T(n) = T(n - 1) + O(1) describes a recursion that does constant work and calls itself on a problem one smaller, such as factorial or linear recursion over an array. Unrolling gives n levels of constant work, so T(n) = Θ(n). The depth is also n, so the stack needs O(n) space.

Step through the factorial call stack and count n calls.

Java
1static long fact(int n) { return n <= 1 ? 1 : n * fact(n - 1); } // T(n) = T(n-1) + O(1) = Theta(n)

Factorial: calls, base case and return path

Interactive · edit the input

Your input

Try

Recursion tree · 1 call

factorial(4)…
Running now
Call

Call factorial(4). It is frame number 1 on the call stack.

Step 1 / 20
Java
1static long factorial(int n) {
2 if (n == 0) return 1; // base case
3 return n * factorial(n - 1); // recursive case
4}

Live state

1 calls

n

4

depth

1

No condition evaluated on this step

Call stack (top first)

  1. factorial(4)
Execution trace (dry run table)Show 20 steps
StepLineStateWhat happens
1L1n=4 depth=1Call factorial(4). It is frame number 1 on the call stack.
2L2n=4n is not 0, so this call cannot answer yet. It needs factorial(3).
3L3n=4Pause factorial(4) and call factorial(3). The pending multiplication by 4 waits on the stack.
4L1n=3 depth=2Call factorial(3). It is frame number 2 on the call stack.
5L2n=3n is not 0, so this call cannot answer yet. It needs factorial(2).
6L3n=3Pause factorial(3) and call factorial(2). The pending multiplication by 3 waits on the stack.
7L1n=2 depth=3Call factorial(2). It is frame number 3 on the call stack.
8L2n=2n is not 0, so this call cannot answer yet. It needs factorial(1).
9L3n=2Pause factorial(2) and call factorial(1). The pending multiplication by 2 waits on the stack.
10L1n=1 depth=4Call factorial(1). It is frame number 4 on the call stack.
11L2n=1n is not 0, so this call cannot answer yet. It needs factorial(0).
12L3n=1Pause factorial(1) and call factorial(0). The pending multiplication by 1 waits on the stack.
13L1n=0 depth=5Call factorial(0). It is frame number 5 on the call stack.
14L2n=0Base case reached: no more recursion. Everything below this point is the return path.
15L2n=0 return=1return 1.
16L3n=1 return=1factorial(1) resumes: 1 × 1 = 1. Return it to the caller.
17L3n=2 return=2factorial(2) resumes: 2 × 1 = 2. Return it to the caller.
18L3n=3 return=6factorial(3) resumes: 3 × 2 = 6. Return it to the caller.
19L3n=4 return=24factorial(4) resumes: 4 × 6 = 24. Return it to the caller.
20L3return=24factorial(4) = 24, using 5 calls and a maximum stack depth of 5.
4.04.2

T(n)=2T(n/2)+O(n)

T(n) = 2T(n/2) + O(n) splits into two halves and does linear work to combine, as in merge sort. There are log₂ n levels and each level costs about n, so T(n) = Θ(n log n).

Open the recursion-tree view: choose this recurrence and read the work per level.

Java
1static void mergeSort(int[] a, int lo, int hi) { // T(n) = 2T(n/2) + O(n)
2 if (hi - lo < 2) return;
3 int mid = (lo + hi) >>> 1;
4 mergeSort(a, lo, mid); mergeSort(a, mid, hi);
5 merge(a, lo, mid, hi); // O(n) work
6}

Recursion tree for a recurrence

Interactive · edit the input

Your input

Try

work per level

empty

total work so far

0
Call

Draw the recursion tree: every node is one call, labelled with its input size. The node's own work is n.

Step 1 / 17
Java
1// T(n) = a * T(n / b) + f(n): each call does f(n) work itself and spawns a smaller calls
2static int solve(int n) {
3 if (n <= 1) return 1; // base case: constant work
4 int work = f(n); // work done in THIS call
5 for (int k = 0; k < a; k++) solve(n / b);
6 return work;
7}
8// total work = sum of f(size) over every node of the recursion tree

Live state

0 calls

n

8

total

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 17 steps
StepLineStateWhat happens
1L1n=8 total=0Draw the recursion tree: every node is one call, labelled with its input size. The node's own work is n.
2L4n=8 total=8 size=8 own=8T(8) does 8 units of its own work. Level 0 has now accumulated 8.
3L4n=8 total=12 size=4 own=4T(4) does 4 units of its own work. Level 1 has now accumulated 4.
4L4n=8 total=14 size=2 own=2T(2) does 2 units of its own work. Level 2 has now accumulated 2.
5L3n=8 total=15 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 1.
6L3n=8 total=16 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 2.
7L4n=8 total=18 size=2 own=2T(2) does 2 units of its own work. Level 2 has now accumulated 4.
8L3n=8 total=19 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 3.
9L3n=8 total=20 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 4.
10L4n=8 total=24 size=4 own=4T(4) does 4 units of its own work. Level 1 has now accumulated 8.
11L4n=8 total=26 size=2 own=2T(2) does 2 units of its own work. Level 2 has now accumulated 6.
12L3n=8 total=27 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 5.
13L3n=8 total=28 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 6.
14L4n=8 total=30 size=2 own=2T(2) does 2 units of its own work. Level 2 has now accumulated 8.
15L3n=8 total=31 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 7.
16L3n=8 total=32 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 8.
17L8n=8 levels=4 calls=15 return=32Total work = 32 for n = 8 across 4 levels: 8 + 8 + 8 + 8. The recurrence solves to Θ(n log n). Compare the level sums: equal sums per level mean (levels × per-level), a shrinking sum means the root dominates, a growing sum means the leaves dominate.
4.04.3

Substitution

The substitution method guesses the solution and proves it by induction. To show T(n) = 2T(n/2) + n is O(n log n), assume T(m) ≤ c·m·log₂ m for m < n. Then T(n) ≤ 2·c·(n/2)·log₂(n/2) + n = c·n·log₂ n - c·n + n ≤ c·n·log₂ n whenever c ≥ 1.

Guess from a recursion tree, then prove. A wrong guess fails in the induction step, which tells you to adjust it.

4.04.4

Recursion tree

A recursion tree draws each call as a node labelled with its own non-recursive cost. Sum the cost per level and then over the levels. For T(n) = 2T(n/2) + n each level costs n and there are log₂ n + 1 levels. For T(n) = T(n - 1) + 1 the tree is a chain of n nodes.

Choose a recurrence below, set n, and read the total and per-level work.

RecurrenceWork per levelLevelsResult
2T(n/2) + nnlog₂ nΘ(n log n)
T(n/2) + 11log₂ nΘ(log n)
T(n/2) + nn, n/2, n/4, …log₂ nΘ(n)
4T(n/2) + nn, 2n, 4n, …log₂ nΘ(n²)
T(n-1) + nn, n-1, …nΘ(n²)

Recursion tree for a recurrence

Interactive · edit the input

Your input

Try

work per level

empty

total work so far

0
Call

Draw the recursion tree: every node is one call, labelled with its input size. The node's own work is n.

Step 1 / 17
Java
1// T(n) = a * T(n / b) + f(n): each call does f(n) work itself and spawns a smaller calls
2static int solve(int n) {
3 if (n <= 1) return 1; // base case: constant work
4 int work = f(n); // work done in THIS call
5 for (int k = 0; k < a; k++) solve(n / b);
6 return work;
7}
8// total work = sum of f(size) over every node of the recursion tree

Live state

0 calls

n

8

total

0

No condition evaluated on this step

Call stack (top first)

  1. empty
Execution trace (dry run table)Show 17 steps
StepLineStateWhat happens
1L1n=8 total=0Draw the recursion tree: every node is one call, labelled with its input size. The node's own work is n.
2L4n=8 total=8 size=8 own=8T(8) does 8 units of its own work. Level 0 has now accumulated 8.
3L4n=8 total=12 size=4 own=4T(4) does 4 units of its own work. Level 1 has now accumulated 4.
4L4n=8 total=14 size=2 own=2T(2) does 2 units of its own work. Level 2 has now accumulated 2.
5L3n=8 total=15 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 1.
6L3n=8 total=16 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 2.
7L4n=8 total=18 size=2 own=2T(2) does 2 units of its own work. Level 2 has now accumulated 4.
8L3n=8 total=19 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 3.
9L3n=8 total=20 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 4.
10L4n=8 total=24 size=4 own=4T(4) does 4 units of its own work. Level 1 has now accumulated 8.
11L4n=8 total=26 size=2 own=2T(2) does 2 units of its own work. Level 2 has now accumulated 6.
12L3n=8 total=27 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 5.
13L3n=8 total=28 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 6.
14L4n=8 total=30 size=2 own=2T(2) does 2 units of its own work. Level 2 has now accumulated 8.
15L3n=8 total=31 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 7.
16L3n=8 total=32 size=1 own=1T(1) does 1 unit of its own work. Level 3 has now accumulated 8.
17L8n=8 levels=4 calls=15 return=32Total work = 32 for n = 8 across 4 levels: 8 + 8 + 8 + 8. The recurrence solves to Θ(n log n). Compare the level sums: equal sums per level mean (levels × per-level), a shrinking sum means the root dominates, a growing sum means the leaves dominate.
4.04.5

Master theorem

For T(n) = aT(n/b) + f(n) with a ≥ 1, b > 1, compare f(n) with n^(log_b a). Case 1: f is polynomially smaller, T = Θ(n^(log_b a)) (leaves dominate). Case 2: f = Θ(n^(log_b a)), T = Θ(n^(log_b a) log n) (every level equal). Case 3: f is polynomially larger and satisfies the regularity condition a·f(n/b) ≤ c·f(n) with c < 1, T = Θ(f(n)) (root dominates).

Enter a, b and f in the tool to see the case and the result.

Recurrencen^(log_b a)CaseResult
T(n/2) + 1 (binary search)n⁰ = 12Θ(log n)
2T(n/2) + n (merge sort)n2Θ(n log n)
8T(n/2) + n²n³1Θ(n³)
T(n/2) + n13Θ(n)
7T(n/2) + n² (Strassen)n^2.8071Θ(n^2.807)

Master theorem calculator

Change a, b and d

T(n) = 2·T(n / 2) + n

Try

Critical exponent log_b a

1

Compare with d

d = 1 = 1

Solution · case 2

Θ(n^1 log n)

Case 2: f(n) = n^1 grows at the same rate as n^1, so every level does about the same work and there are log n levels.

Work per level of the recursion tree (n = 2^7 = 128)

LevelSubproblemsSize eachWork eachLevel total
01128128128
126464128
243232128
381616128
41688128
53244128
66422128
712811128

Case 1: level totals grow toward the leaves. Case 2: they stay equal. Case 3: they shrink away from the root. Add them up and the biggest term decides the answer. This table assumes f(n) = n^d with no log factor.

4.04.6

Akra-Bazzi overview

The Akra-Bazzi method handles recurrences the Master theorem cannot, such as unequal splits: T(n) = Σ aᵢ T(bᵢ n) + g(n). Find p with Σ aᵢ bᵢᵖ = 1; then T(n) = Θ(nᵖ (1 + ∫₁ⁿ g(u) / uᵖ⁺¹ du)).

Example: T(n) = T(n/3) + T(2n/3) + n has (1/3)ᵖ + (2/3)ᵖ = 1 at p = 1, and the integral gives log n, so T(n) = Θ(n log n). This is the cost of a quick sort whose pivots always split 1:2.

Practice ladder

Now try it yourself

Easy

  • Write the recurrence for binary search and solve it
  • Solve T(n) = T(n - 1) + n

Medium

  • Use the Master theorem on T(n) = 4T(n/2) + n² log n (hint: it is outside the three simple cases)
  • Prove by substitution that T(n) = T(n/2) + 1 is O(log n)

Advanced

  • Solve T(n) = T(n/5) + T(7n/10) + n with Akra-Bazzi

Key takeaways

  • T(n - 1) + O(1) is linear; 2T(n/2) + O(n) is n log n.
  • Recursion trees give the guess; substitution proves it.
  • The Master theorem compares f(n) with n^(log_b a).