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 Oupper 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):

ClassNameExample
ConstantHash table lookup, array indexing
LogarithmicBinary search, balanced BST operations
LinearLinear search, single array traversal
LinearithmicMerge sort, quicksort (avg), heap sort
QuadraticBubble sort, selection sort, insertion sort
CubicNaïve matrix multiply, Floyd-Warshall
ExponentialBrute-force subset enumeration
FactorialBrute-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, then i < n runs 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:

  1. 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.

  2. 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.