Asymptotic Complexity & Recurrences
Every other algorithms chapter depends on this one, because every claim about an algorithm's cost is stated in asymptotic notation and most of them are derived from a recurrence.
Asymptotic notation is a statement about growth, not about speed. Saying an algorithm is says nothing about whether it takes a microsecond or an hour on any particular input. It says that as the input grows without bound, the running time grows no faster than the square.
The notation deliberately discards constants and lower-order terms, because those depend on the machine, the compiler and the implementation, while the growth rate depends on the algorithm. Discarding them is what makes the claim portable.
That discarding has a cost worth stating plainly. An algorithm can be slower than an one on every input you will ever run, if its constant is large enough. Asymptotics tell you which wins eventually, not which wins today.
The second organising fact is that a recursive algorithm's cost is a recurrence, and a recurrence is read directly off the code. Count the recursive calls, note the subproblem size, add the work outside the calls. Everything after that is solving.
1. The Five Notations
Each notation bounds a function in a different direction, and the distinction is examined directly.
| Notation | Meaning | Analogy |
|---|---|---|
| Grows no faster than | At most | |
| Grows no slower than | At least | |
| Grows exactly like | Exactly | |
| Grows strictly slower than | Less than | |
| Grows strictly faster than | Greater than |
Formally, means there exist positive constants and such that
for all .
The two constants are what make the definition work. The constant absorbs any multiplicative factor and allows the bound to fail for small inputs, which is exactly why lower-order terms are irrelevant.
is both and simultaneously, so proving a tight bound means proving two inequalities rather than one.
Two remarks catch people out. is an upper bound and need not be tight, so writing that insertion sort is is technically true and useless. And describes a function, not an algorithm, so an algorithm has different bounds for best, average and worst case, and stating one without saying which is ambiguous.
The relation is not symmetric, so writing is meaningless. The equals sign here is a historical abuse of notation for set membership.
2. Comparing Growth Rates
The hierarchy is worth memorising, because most questions are asking where a function sits in it.
Three rules settle almost every comparison.
Any polynomial beats any polylogarithm. eventually overtakes , however absurd that looks for practical .
Any exponential beats any polynomial. eventually overtakes .
The base of a logarithm is irrelevant, since changing base multiplies by a constant, which absorbs. So and are both . The base of an exponential is not irrelevant: and differ by an exponential factor, not a constant.
When the hierarchy does not settle a comparison directly, take logarithms of both functions and compare those, or apply L'Hopital's rule to the ratio.
A common trap is against . Taking logarithms gives against , and the second is larger, so grows faster.
3. Analysing Loops
For iterative code, count how many times the innermost statement executes.
A loop running from 1 to with a constant increment executes times. A loop that multiplies its counter by a constant executes times, because the counter reaches after about multiplications.
Nested loops multiply when independent, but not when the inner bound depends on the outer counter.
For an inner loop running to the outer counter, the total is the sum , which is — the same as two independent loops, but the constant is halved.
A loop whose counter is squared each time executes times, since the exponent doubles and reaching needs doublings.
Conditional work inside a loop is bounded above by the worst case, but a tighter analysis sometimes shows the worst case cannot happen on every iteration — which is where amortised analysis begins.
4. Recurrences: Substitution and Recursion Trees
A recurrence for a divide-and-conquer algorithm has the shape , where is the number of subproblems, their size, and the work of dividing and combining.
The substitution method guesses a bound and proves it by induction. It is the only fully general method and the only one that produces a proof, but it requires the guess.
A frequent failure is to guess correctly and still fail the induction because the inductive hypothesis is too weak. Strengthening the hypothesis by subtracting a lower-order term often rescues it, which feels backwards and is standard practice.
The recursion tree method draws the recursion and sums the work level by level. It is the reliable way to generate the guess that substitution then proves.
At level there are nodes, each of size , so the work at that level is . The tree has levels, and the leaves number .
Three outcomes are possible and they correspond exactly to the three master-theorem cases. Either the root dominates, or the leaves dominate, or every level contributes equally.
5. The Master Theorem
For with and , compare against .
That exponent is the critical quantity, and it is the total work at the leaves of the recursion tree.
| Case | Condition | Result |
|---|---|---|
| 1 | ||
| 2 | , | |
| 3 | and regularity |
Case 1 is leaf-dominated: the work shrinks going down the tree so fast that the leaves carry it all. Case 3 is root-dominated: the top level carries it all. Case 2 is balanced: every level contributes the same, and the extra logarithm counts the levels.
Case 3 carries a regularity condition that for some , which ensures the work genuinely shrinks going down. It holds for every polynomial that appears in practice and is checked only when is unusual.
The theorem does not always apply. If is larger than but not polynomially larger — by a factor of only , say — then no case fits, and a recursion tree is required.
The standard example of that gap is , which falls between cases 1 and 2.
6. Amortised Analysis
Amortised analysis bounds the average cost per operation over a worst-case sequence. It is not average-case analysis, because no probability is involved: the bound holds for every sequence, not for a typical one.
Three methods give the same answer with different bookkeeping.
The aggregate method bounds the total cost of operations and divides by . It is the simplest and is usually enough.
The accounting method charges each operation more than it costs and banks the surplus to pay for later expensive operations, requiring only that the balance never goes negative.
The potential method defines a function of the data structure's state and charges each operation its actual cost plus the change in potential.
The potential must be chosen so that it starts at zero, never goes negative, and rises during cheap operations by enough to pay for the expensive ones later. For a doubling array, the potential is typically twice the number of elements past the halfway mark, which reaches the array's size exactly when a copy is due.
All three methods must give the same amortised bound, since they are bookkeeping devices rather than different analyses. The aggregate method is fastest when the total is easy to sum, and the potential method is preferred when several operations interact.
The canonical example is a dynamic array that doubles when full. A single append can cost when a copy happens, but copies are rare: after a copy at size , the next occurs at size . Summing the copy costs over appends gives , so the amortised cost per append is .
The reason doubling works and adding a constant does not is that doubling makes the gaps between copies grow geometrically, while adding a fixed amount makes them constant, giving copies of average cost and a quadratic total.
7. Worked Examples
Example 1. Order these by growth rate: , , , , .
Simplify what can be simplified first. with a base-2 logarithm is exactly , so it is linear.
For , take logarithms: . Compare with , whose logarithm is , dominated by . Since exceeds , the function grows faster than .
Compare with , whose logarithm is . Dividing both logarithms by reduces the comparison to against the constant , and exceeds for any reasonably large . So the quasi-polynomial is larger.
Finally, dominates everything here, since its logarithm is by Stirling's approximation, which exceeds by a wide margin.
The order is .
The lesson is that taking logarithms is the reliable method whenever the hierarchy does not settle a comparison by inspection, because it converts exponents into products that can be compared term by term.
Example 2. Solve using the master theorem.
Identify , , and .
Compute the critical exponent: , so .
Compare against . Since grows faster than , and , we have for .
That is case 3, so check regularity: is for some ?
, so and the condition holds.
Therefore .
Example 3. Solve .
Identify , , so .
Compare against . It is smaller, which suggests case 1 — but case 1 requires to be polynomially smaller, that is for some positive .
It is not. The ratio grows more slowly than any positive power of , so no works. The master theorem does not apply.
Use a recursion tree. At level there are nodes of size , so the level's work is
Summing over from 0 to gives times the sum of , which is times the harmonic series up to .
That harmonic sum is , so .
This is the standard example of the master theorem's gap, and recognising the gap is more of the answer than the arithmetic.
Example 4. A loop runs for (i = 1; i < n; i = i * 3) containing an inner loop running for (j = 0; j < i; j++). What is the total cost?
The outer counter takes values up to , so it runs times.
The inner loop runs times for each outer value, so the total is the sum of the outer values:
This is a geometric series with ratio 3, and its sum is dominated by its last term, which is .
Precisely, the sum equals where , which is .
The total cost is , not .
The trap is multiplying the loop counts. The outer loop runs times and the inner runs up to times, which suggests — but the inner loop reaches only on the final iteration, and the geometric series is dominated by that single term.
Example 5. Prove by substitution that is .
Guess for some constant and all .
Assume it holds for : .
Substitute into the recurrence:
For this to be at most , we need , that is .
So the bound holds with , provided the base case is satisfied — and for , requires the constant to be chosen large enough, which is always permitted since may be raised.
The induction closes, so .
Note the shape of the argument. The recurrence produced an extra term that had to absorb the , and that absorption is what fixed the constant. A guess of would have failed here precisely because no such slack appears.
Example 6. A dynamic array doubles its capacity when full. Show that appends cost in total, and explain why growing by a fixed increment does not.
Under doubling, a copy occurs when the array is full, at sizes up to .
A copy at size costs operations, so the total copying cost is
because a geometric series with ratio 2 sums to less than twice its largest term.
Adding the appends themselves gives at most operations for appends, so the amortised cost per append is .
Now consider growing by a fixed increment . Copies occur at sizes , so there are copies, and the copy at size costs .
The total is .
That is for any fixed , so the amortised cost per append is rather than .
The difference is geometric versus arithmetic growth in the gaps between copies. Doubling makes each copy twice as rare as it is expensive, so the two effects cancel; a fixed increment makes copies equally frequent while they grow steadily more expensive.
Summary
Asymptotic notation describes growth, not speed, and discards constants and lower-order terms precisely because those depend on the machine rather than the algorithm.
is an upper bound, a lower bound, both, and and their strict versions. An upper bound need not be tight, and the notation describes a function rather than an algorithm, so best, average and worst case must be distinguished.
Any polynomial beats any polylogarithm and any exponential beats any polynomial. Logarithm bases are irrelevant; exponential bases are not. When the hierarchy does not settle a comparison, take logarithms.
Loop analysis counts innermost executions. A multiplying counter gives iterations, a squaring counter gives , and nested loops multiply only when independent.
A divide-and-conquer recurrence is read off the code: number of calls, subproblem size, and work outside. Recursion trees generate the guess and substitution proves it, sometimes needing a strengthened hypothesis.
The master theorem compares against : polynomially smaller gives leaf domination, equal up to logs gives a balanced tree with one extra log, and polynomially larger with regularity gives root domination.
The theorem fails when differs by a non-polynomial factor, and is the standard example, solving to by recursion tree.
Amortised analysis bounds the average over a worst-case sequence and involves no probability. Doubling a dynamic array gives amortised appends because the gaps between copies grow geometrically, while a fixed increment gives .