DSA Notes
Master asymptotic analysis — the complete mathematical framework for analyzing algorithm complexity. Learn to solve recurrence relations using the substitution method, recursion tree method, and Master Theorem. Includes full worked examples for all common algorithm patterns.
Introduction
You now have three tools: Big O (upper bound), Big Omega (lower bound), and Big Theta (tight bound). Asymptotic analysis is the complete framework that uses these tools together to formally characterize algorithm behavior.
But for recursive algorithms — which make up a large portion of important algorithms — simply counting loops is not enough. Recursive algorithms express their running time in terms of their running time on smaller inputs. This creates a recurrence relation, and solving it requires specific mathematical techniques.
This lesson covers:
- The complete framework of asymptotic analysis
- How to set up recurrence relations for any recursive algorithm
- Three methods for solving recurrences: substitution, recursion tree, and Master Theorem
- How to apply the complete analysis to every major algorithm type
Setting Up Recurrence Relations
A recurrence relation is an equation that defines a function in terms of its value at smaller inputs. For algorithm analysis:
T(n) = the number of operations performed on an input of size n
For recursive algorithms, T(n) is expressed in terms of T(smaller values).
The General Form
Most recursive algorithms produce recurrences of the form:
Example — Binary Search
Setting up the recurrence:
- One recursive call: a = 1
- Input size halves: b = 2 (call on n/2 elements)
- Work outside the call: 1 comparison = O(1)
Recurrence: T(n) = T(n/2) + O(1), T(1) = O(1)
Example — Merge Sort
Setting up the recurrence:
- Two recursive calls: a = 2
- Input size halves: b = 2
- Work outside: O(n) for merging
Recurrence: T(n) = 2T(n/2) + n, T(1) = O(1)
Example — Naive Fibonacci
def fibonacci(n):
if n <= 1:
return n # Base case: O(1)
return fibonacci(n - 1) + fibonacci(n - 2) # Two recursive callsSetting up the recurrence:
- Two recursive calls, but they do NOT halve the input equally
- The calls are on inputs n-1 and n-2 (subtracting 1 or 2, not dividing)
Recurrence: T(n) = T(n-1) + T(n-2) + O(1), T(0) = T(1) = O(1)
This is not in the standard divide-and-conquer form. It requires a different solution method.
Method 1 — The Substitution Method
The substitution method involves:
- Guessing the form of the solution
- Using mathematical induction to prove it
This method is powerful but requires insight to make a good initial guess.
Example — Solving T(n) = T(n/2) + 1
Guess: T(n) = O(log n), i.e., T(n) ≤ c · log n for some constant c.
Prove T(n) ≤ c · log n by induction:
*Assume:* T(k) ≤ c · log k for all k < n (inductive hypothesis)
*Prove for n:*
Conclusion: T(n) = O(log n) ✓
Example — Solving T(n) = 2T(n/2) + n
Guess: T(n) = O(n log n)
Prove T(n) ≤ c · n · log n:
*Assume:* T(k) ≤ c · k · log k for all k < n
*Prove for n:*
Conclusion: T(n) = O(n log n) ✓
Tips for Guessing
- If the recurrence subtracts a constant (T(n-1)), the solution is often polynomial: T(n) = O(n^k)
- If the recurrence divides by a constant (T(n/b)), the solution is often logarithmic or polynomial-log: T(n) = O(log n) or O(n log n)
- If two recursive calls on nearly full-size input (T(n-1) + T(n-2)), the solution is often exponential: T(n) = O(2ⁿ)
Method 2 — The Recursion Tree Method
The recursion tree method draws out the recursive call structure as a tree, calculates the work done at each level, and sums across all levels.
Steps:
- Draw the recursion tree: each node represents one recursive call
- Label each node with the non-recursive work at that level
- Sum the work at each level
- Sum across all levels
Example — T(n) = 2T(n/2) + n (Merge Sort)
Step 1 — Draw the tree:
| Level 0 | n Work: n |
| Level 1 | n/2 n/2 Work: n/2 + n/2 = n |
| Level 2 | n/4 n/4 n/4 n/4 Work: 4 × n/4 = n |
| Level k | 2^k nodes, each of size n/2^k Work: 2^k × n/2^k = n |
| Level log n | n nodes, each of size 1 Work: n × O(1) = n |
Step 2 — Identify the depth: The tree has depth log₂(n), because we keep halving until we reach size 1.
Step 3 — Sum the levels:
- Each of the log n + 1 levels contributes n work
- Total = n × (log n + 1) = n log n + n = O(n log n)
Answer: T(n) = O(n log n) ✓
Example — T(n) = 3T(n/4) + n²
Step 1 — Draw the tree:
| Level 0 | n² Work: n² |
| Level 1 | (n/4)² (n/4)² (n/4)² Work: 3(n/4)² = 3n²/16 |
| Level 2 | nine nodes of size (n/16)² Work: 9(n/16)² = 9n²/256 |
| General level k | 3^k nodes of size n/4^k |
Step 2 — Identify the depth: We divide by 4 each time: depth = log₄(n)
Step 3 — Sum the levels:
Answer: T(n) = O(n²)
The non-recursive work (n²) dominates here — the recursive part contributes less than the top level.
Method 3 — The Master Theorem
The Master Theorem provides direct, formulaic solutions for a large class of recurrences of the form:
T(n) = a · T(n/b) + f(n) where a ≥ 1, b > 1
The Three Cases
Let **c\* = log_b(a)** (the "critical exponent").
Case 1 — Recursive work dominates: If f(n) = O(n^(c* - ε)) for some ε > 0 (f grows slower than n^c*)
Then: **T(n) = Θ(n^c*)**
The leaves of the recursion tree do the most work.
Case 2 — Work is balanced at all levels: If f(n) = Θ(n^c* · log^k n) for some k ≥ 0 (f grows at the same rate as n^c*)
Then: **T(n) = Θ(n^c* · log^(k+1) n)**
Common subcase (k=0): If f(n) = Θ(n^c*), then T(n) = Θ(n^c* · log n)
Case 3 — Non-recursive work dominates: If f(n) = Ω(n^(c* + ε)) for some ε > 0 (f grows faster than n^c*) AND f(n/b) ≤ cf(n) for some c < 1 (regularity condition)
Then: T(n) = Θ(f(n))
The root of the recursion tree does the most work.
Applying the Master Theorem — Five Complete Examples
Example 1 — Binary Search: T(n) = T(n/2) + 1
Example 2 — Merge Sort: T(n) = 2T(n/2) + n
Example 3 — T(n) = 4T(n/2) + n (example)
Example 4 — T(n) = 4T(n/2) + n² (example)
Example 5 — T(n) = 4T(n/2) + n³ (example)
When Master Theorem Does Not Apply
The Master Theorem only applies when the recurrence is in the form T(n) = aT(n/b) + f(n). It does NOT apply to:
Linear recurrences (T(n-1), T(n-2), etc.):
| T(n) = T(n-1) + n | Solve by expansion: O(n²) |
| T(n) = T(n-1) + 1 | Solve by expansion: O(n) |
| T(n) = T(n-1) + T(n-2) | Fibonacci-style: O(2ⁿ) |
When f(n) falls in the gap between cases:
Solving Non-Standard Recurrences
T(n) = T(n-1) + n — Factorial/Simple Recursion
T(n) = T(n-1) + 1 — Linear Recursion
T(n) = 2T(n-1) + 1 — Tower of Hanoi
Complete Analysis Reference
| Recurrence | Algorithm Example | Solution |
|---|---|---|
| T(n) = T(n/2) + O(1) | Binary Search | Θ(log n) |
| T(n) = T(n-1) + O(1) | Factorial | Θ(n) |
| T(n) = 2T(n/2) + O(n) | Merge Sort | Θ(n log n) |
| T(n) = 2T(n/2) + O(1) | Tree traversal | Θ(n) |
| T(n) = T(n-1) + O(n) | Insertion Sort worst | Θ(n²) |
| T(n) = 2T(n-1) + O(1) | Tower of Hanoi | Θ(2ⁿ) |
| T(n) = T(n-1) + T(n-2) | Naive Fibonacci | Θ(φⁿ) ≈ Θ(1.618ⁿ) |
| T(n) = 7T(n/2) + O(n²) | Strassen Matrix | Θ(n^2.807) |
| T(n) = 4T(n/2) + O(n²) | Example | Θ(n² log n) |
| T(n) = 9T(n/3) + O(n²) | Example | Θ(n² log n) |
Summary
Asymptotic analysis is the complete mathematical framework for characterizing algorithm efficiency:
- Three notations: O (upper/worst), Ω (lower/best), Θ (tight/average)
- Recurrence relations: Express recursive algorithm complexity as T(n) = aT(n/b) + f(n)
- Three solving methods:
- Substitution: Guess and prove by induction — requires insight, very rigorous
- Recursion Tree: Draw the tree, sum level-by-level — visual and intuitive
- Master Theorem: Direct formula for T(n) = aT(n/b) + f(n) — fastest method when applicable
- The Master Theorem has three cases based on whether f(n) is smaller than, equal to, or larger than n^(log_b(a))
- For non-divide-and-conquer recurrences, use expansion (unrolling) or the recursion tree
Mastery of asymptotic analysis is what separates engineers who can reason about algorithm performance from those who can only measure it empirically after the fact.
*Next Lesson: Best, Average, and Worst Case Analysis — Applying asymptotic analysis to concrete algorithms and understanding how input characteristics affect performance.*
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Asymptotic Analysis — Complete Framework with Recurrence Relations and Master Theorem.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Data Structures & Algorithms topic.
Search Terms
data-structures-algorithms, data structures & algorithms, data, structures, algorithms, algorithm, analysis, asymptotic
Related Data Structures & Algorithms Topics