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 everyday talk “Big O” gets used loosely to mean “tight bound,” but the formal distinction matters when you’re actually doing 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.
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.