Loading
Loading
Complexity Analysis
Why we count operations instead of timing code, and what that buys you in an interview.
By the end of this chapter
Complexity analysis is a way of describing how much work an algorithm does as a function of how much input it is given. It deliberately ignores the speed of your machine, the language you wrote it in, and how good the compiler is.
That sounds like throwing away useful information. It is the opposite: those details change constantly, and the shape of the growth does not.
Suppose two functions solve the same problem. On a thousand items, one takes 2 milliseconds and the other takes 1. The second looks twice as good. Now run both on a million items.
| Input size | Linear approach | Quadratic approach |
|---|---|---|
| 1,000 | 2 ms | 1 ms |
| 10,000 | 20 ms | 100 ms |
| 100,000 | 200 ms | 10 seconds |
| 1,000,000 | 2 seconds | ~17 minutes |
The slower-looking function wins at every size that matters. Its advantage at a thousand items was a constant factor; the other's disadvantage was structural. Constant factors stop mattering; growth rates never do.
Concept
Ask how many times the innermost operation runs, expressed in terms of the input size n. That count is the algorithm's cost. Everything else — CPU speed, language, memory layout — scales that count by some constant, and constants are exactly what this analysis discards.
In practice this becomes a habit: read a loop and ask how many times it runs; read a nested loop and multiply; read a recursive call and ask how the problem shrinks.
An array of n numbers, searching for a targetcompare element 01 operationcompare element 12 operations…the loop may run all the waycompare element n-1n operations totaln comparisons in the worst caseThe target might be first, in which case one comparison suffices. It might be absent, in which case every element is checked. Complexity analysis reports the worst case by default, because that is the guarantee you can rely on.
def has_duplicate(nums): for i in range(len(nums)): # runs n times for j in range(i + 1, len(nums)): # runs up to n times if nums[i] == nums[j]: # the innermost operation return True return FalseThe inner comparison runs roughly n²/2 times. We drop the ½ — it is a constant — and call this quadratic.
Interviewers give constraints for a reason. They are telling you which complexities are acceptable.
| Constraint on n | What usually fits |
|---|---|
| n ≤ 20 | Exponential — 2ⁿ or n! search |
| n ≤ 3,000 | Quadratic — O(n²) |
| n ≤ 200,000 | O(n log n) — sorting, heaps, binary search per element |
| n ≤ 10,000,000 | Linear or better |
Quick quiz
Sign in to keep a note here. It is free, and nobody else can see it.
Sign inWhat this chapter was for:
Before moving on: take the quiz above, and try 1 practice problem.