Big O notation describes how a function grows asymptotically — what an algorithm’s time function looks like for large inputs, ignoring constants and lower-order terms. Writing means: for large enough , is at most a constant times .
Formally:
We pick a constant and a threshold . For all inputs bigger than , is bounded above by . The constants and are not unique — many pairs work.
Worked example
Show that is .
We need to find and such that for all , .
Try :
Since for all , the inequality holds when , i.e. .
So for and , for all . Therefore .
Note: doesn’t mean grows as fast as . It means grows no faster than . In fact is , which is a tighter bound. Both statements are true; the tighter one is more informative.
The big three
Big O is one of three related notations:
- Big O — upper bound. means grows no faster than .
- Big Omega () — lower bound. means grows at least as fast as .
- Big Theta () — tight bound. means grows exactly as (i.e., both and ).
These are bounds on a function’s growth rate; they’re orthogonal to best/worst/average case. You can give a Big-O bound on the worst-case running time (most common), or on the best-case, or on the average-case. The notations describe how a chosen function scales — not which input you picked to measure.
In practice, “Big O” is used loosely to mean “tight bound” in everyday discussion — but the formal distinction matters for analysis. See Big Omega notation for lower-bound use (problem complexity arguments) and Big Theta notation for the precise tight-bound notation.
Properties
When combining functions:
- Sum: . The larger term dominates.
- Product: . So .
- Polynomials: . Drop everything but the leading term, drop the leading coefficient.
- Constants: for any constant .
Common growth classes
Ranked from slowest to fastest growth (top is best for an algorithm):
| Class | Name | Example |
|---|---|---|
| Constant | Hash table lookup, array indexing | |
| Logarithmic | Binary search, balanced BST operations | |
| Linear | Linear search, single array traversal | |
| Linearithmic | Merge sort, quicksort (avg), heap sort | |
| Quadratic | Bubble sort, selection sort, insertion sort | |
| Cubic | Naïve matrix multiply, Floyd-Warshall | |
| Exponential | Brute-force subset enumeration | |
| Factorial | Brute-force permutations, traveling salesman |
The gap between any two adjacent classes is huge for large . An algorithm processing 1,000,000 elements might take 1 second; an algorithm on the same input takes 12 days.
Why constants don’t matter
If algorithm A has running time and algorithm B has running time , both are . They have the same asymptotic time complexity. For very large , B is roughly faster, but both grow as — and that’s what matters when comparing to a different complexity class.
If algorithm C is (linear with a huge constant), then for small , the algorithms might actually be faster — the constants matter. But for , C wins, and the gap grows from there. Big O captures eventual behavior, which is usually what dominates in practice.
Worked example: sum function
int sum(int a[], int n) {
int s = 0;
for (int i = 0; i < n; i++) {
s = s + a[i];
}
return s;
}Frequency count by line:
int s = 0;— 1.i = 0— 1, theni < nruns times (one extra for the failing check).s = s + a[i]— runs times.return s;— 1.
Total: .
This is — the constant and the additive disappear when we go to Big O. The function grows linearly with the input size.
What Big O isn’t
Two common misunderstandings:
-
Big O isn’t the time taken. It’s the growth rate. Two algorithms can take very different actual times depending on constants and the specific work per iteration.
-
Big O isn’t a unique answer. Saying doesn’t preclude or . They’re all valid upper bounds; the tightest one is most useful.
When someone says “the algorithm is ,” they almost always mean it’s tightly — but they’re informally using the more familiar Big O. Context disambiguates.