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
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.
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 inputRecursion tree · 1 call
Call factorial(4). It is frame number 1 on the call stack.
1static long factorial(int n) {2if (n == 0) return 1; // base case3return n * factorial(n - 1); // recursive case4}
Live state
n
4
depth
1
Call stack (top first)
- factorial(4)
Execution trace (dry run table)Show 20 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | n=4 depth=1 | Call factorial(4). It is frame number 1 on the call stack. |
| 2 | L2 | n=4 | n is not 0, so this call cannot answer yet. It needs factorial(3). |
| 3 | L3 | n=4 | Pause factorial(4) and call factorial(3). The pending multiplication by 4 waits on the stack. |
| 4 | L1 | n=3 depth=2 | Call factorial(3). It is frame number 2 on the call stack. |
| 5 | L2 | n=3 | n is not 0, so this call cannot answer yet. It needs factorial(2). |
| 6 | L3 | n=3 | Pause factorial(3) and call factorial(2). The pending multiplication by 3 waits on the stack. |
| 7 | L1 | n=2 depth=3 | Call factorial(2). It is frame number 3 on the call stack. |
| 8 | L2 | n=2 | n is not 0, so this call cannot answer yet. It needs factorial(1). |
| 9 | L3 | n=2 | Pause factorial(2) and call factorial(1). The pending multiplication by 2 waits on the stack. |
| 10 | L1 | n=1 depth=4 | Call factorial(1). It is frame number 4 on the call stack. |
| 11 | L2 | n=1 | n is not 0, so this call cannot answer yet. It needs factorial(0). |
| 12 | L3 | n=1 | Pause factorial(1) and call factorial(0). The pending multiplication by 1 waits on the stack. |
| 13 | L1 | n=0 depth=5 | Call factorial(0). It is frame number 5 on the call stack. |
| 14 | L2 | n=0 | Base case reached: no more recursion. Everything below this point is the return path. |
| 15 | L2 | n=0 return=1 | return 1. |
| 16 | L3 | n=1 return=1 | factorial(1) resumes: 1 × 1 = 1. Return it to the caller. |
| 17 | L3 | n=2 return=2 | factorial(2) resumes: 2 × 1 = 2. Return it to the caller. |
| 18 | L3 | n=3 return=6 | factorial(3) resumes: 3 × 2 = 6. Return it to the caller. |
| 19 | L3 | n=4 return=24 | factorial(4) resumes: 4 × 6 = 24. Return it to the caller. |
| 20 | L3 | return=24 | factorial(4) = 24, using 5 calls and a maximum stack depth of 5. |
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.
1static void mergeSort(int[] a, int lo, int hi) { // T(n) = 2T(n/2) + O(n)2if (hi - lo < 2) return;3int mid = (lo + hi) >>> 1;4mergeSort(a, lo, mid); mergeSort(a, mid, hi);5merge(a, lo, mid, hi); // O(n) work6}
Recursion tree for a recurrence
Interactive · edit the inputwork per level
total work so far
Draw the recursion tree: every node is one call, labelled with its input size. The node's own work is n.
1// T(n) = a * T(n / b) + f(n): each call does f(n) work itself and spawns a smaller calls2static int solve(int n) {3if (n <= 1) return 1; // base case: constant work4int work = f(n); // work done in THIS call5for (int k = 0; k < a; k++) solve(n / b);6return work;7}8// total work = sum of f(size) over every node of the recursion tree
Live state
n
8
total
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 17 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | n=8 total=0 | Draw the recursion tree: every node is one call, labelled with its input size. The node's own work is n. |
| 2 | L4 | n=8 total=8 size=8 own=8 | T(8) does 8 units of its own work. Level 0 has now accumulated 8. |
| 3 | L4 | n=8 total=12 size=4 own=4 | T(4) does 4 units of its own work. Level 1 has now accumulated 4. |
| 4 | L4 | n=8 total=14 size=2 own=2 | T(2) does 2 units of its own work. Level 2 has now accumulated 2. |
| 5 | L3 | n=8 total=15 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 1. |
| 6 | L3 | n=8 total=16 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 2. |
| 7 | L4 | n=8 total=18 size=2 own=2 | T(2) does 2 units of its own work. Level 2 has now accumulated 4. |
| 8 | L3 | n=8 total=19 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 3. |
| 9 | L3 | n=8 total=20 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 4. |
| 10 | L4 | n=8 total=24 size=4 own=4 | T(4) does 4 units of its own work. Level 1 has now accumulated 8. |
| 11 | L4 | n=8 total=26 size=2 own=2 | T(2) does 2 units of its own work. Level 2 has now accumulated 6. |
| 12 | L3 | n=8 total=27 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 5. |
| 13 | L3 | n=8 total=28 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 6. |
| 14 | L4 | n=8 total=30 size=2 own=2 | T(2) does 2 units of its own work. Level 2 has now accumulated 8. |
| 15 | L3 | n=8 total=31 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 7. |
| 16 | L3 | n=8 total=32 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 8. |
| 17 | L8 | n=8 levels=4 calls=15 return=32 | Total 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. |
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.
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.
| Recurrence | Work per level | Levels | Result |
|---|---|---|---|
| 2T(n/2) + n | n | log₂ n | Θ(n log n) |
| T(n/2) + 1 | 1 | log₂ n | Θ(log n) |
| T(n/2) + n | n, n/2, n/4, … | log₂ n | Θ(n) |
| 4T(n/2) + n | n, 2n, 4n, … | log₂ n | Θ(n²) |
| T(n-1) + n | n, n-1, … | n | Θ(n²) |
Recursion tree for a recurrence
Interactive · edit the inputwork per level
total work so far
Draw the recursion tree: every node is one call, labelled with its input size. The node's own work is n.
1// T(n) = a * T(n / b) + f(n): each call does f(n) work itself and spawns a smaller calls2static int solve(int n) {3if (n <= 1) return 1; // base case: constant work4int work = f(n); // work done in THIS call5for (int k = 0; k < a; k++) solve(n / b);6return work;7}8// total work = sum of f(size) over every node of the recursion tree
Live state
n
8
total
0
Call stack (top first)
- empty
Execution trace (dry run table)Show 17 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | n=8 total=0 | Draw the recursion tree: every node is one call, labelled with its input size. The node's own work is n. |
| 2 | L4 | n=8 total=8 size=8 own=8 | T(8) does 8 units of its own work. Level 0 has now accumulated 8. |
| 3 | L4 | n=8 total=12 size=4 own=4 | T(4) does 4 units of its own work. Level 1 has now accumulated 4. |
| 4 | L4 | n=8 total=14 size=2 own=2 | T(2) does 2 units of its own work. Level 2 has now accumulated 2. |
| 5 | L3 | n=8 total=15 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 1. |
| 6 | L3 | n=8 total=16 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 2. |
| 7 | L4 | n=8 total=18 size=2 own=2 | T(2) does 2 units of its own work. Level 2 has now accumulated 4. |
| 8 | L3 | n=8 total=19 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 3. |
| 9 | L3 | n=8 total=20 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 4. |
| 10 | L4 | n=8 total=24 size=4 own=4 | T(4) does 4 units of its own work. Level 1 has now accumulated 8. |
| 11 | L4 | n=8 total=26 size=2 own=2 | T(2) does 2 units of its own work. Level 2 has now accumulated 6. |
| 12 | L3 | n=8 total=27 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 5. |
| 13 | L3 | n=8 total=28 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 6. |
| 14 | L4 | n=8 total=30 size=2 own=2 | T(2) does 2 units of its own work. Level 2 has now accumulated 8. |
| 15 | L3 | n=8 total=31 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 7. |
| 16 | L3 | n=8 total=32 size=1 own=1 | T(1) does 1 unit of its own work. Level 3 has now accumulated 8. |
| 17 | L8 | n=8 levels=4 calls=15 return=32 | Total 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. |
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.
| Recurrence | n^(log_b a) | Case | Result |
|---|---|---|---|
| T(n/2) + 1 (binary search) | n⁰ = 1 | 2 | Θ(log n) |
| 2T(n/2) + n (merge sort) | n | 2 | Θ(n log n) |
| 8T(n/2) + n² | n³ | 1 | Θ(n³) |
| T(n/2) + n | 1 | 3 | Θ(n) |
| 7T(n/2) + n² (Strassen) | n^2.807 | 1 | Θ(n^2.807) |
Master theorem calculator
Change a, b and dT(n) = 2·T(n / 2) + n
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)
| Level | Subproblems | Size each | Work each | Level total | |
|---|---|---|---|---|---|
| 0 | 1 | 128 | 128 | 128 | |
| 1 | 2 | 64 | 64 | 128 | |
| 2 | 4 | 32 | 32 | 128 | |
| 3 | 8 | 16 | 16 | 128 | |
| 4 | 16 | 8 | 8 | 128 | |
| 5 | 32 | 4 | 4 | 128 | |
| 6 | 64 | 2 | 2 | 128 | |
| 7 | 128 | 1 | 1 | 128 |
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.
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).