The complexity classes you will actually meet

Seven growth rates, what produces each one, and where the cliff edges are.

Almost everything you write falls into one of seven classes. Learn what causes each one and you can read complexity off the shape of the code.

Class Name Caused by n = 1,000,000
O(1) Constant Direct index, hash lookup, arithmetic 1
O(log n) Logarithmic Halving the search space each step ~20
O(n) Linear One pass over the input 1,000,000
O(n log n) Linearithmic Sorting; divide and conquer with linear merging ~20,000,000
O(n²) Quadratic Nested loops over the same input 10¹²
O(2ⁿ) Exponential Trying both choices at every step beyond hopeless
O(n!) Factorial Generating every permutation beyond hopeless

The cliff edges

Between O(n log n) and O(n²) is the line that separates code that scales from code that does not. Above O(n²), inputs must be tiny: O(2ⁿ) is fine at n = 20 and impossible at n = 60.

Recognising them in code

arr[i];                                   // O(1)

while (n > 1) n = Math.floor(n / 2);      // O(log n) — n halves

for (const x of arr) { }                  // O(n)

arr.sort((a, b) => a - b);                // O(n log n)

for (const a of arr) for (const b of arr) { }   // O(n²)

function subsets(i) {                     // O(2ⁿ) — two branches, depth n
  if (i === arr.length) return;
  subsets(i + 1);                         // exclude
  subsets(i + 1);                         // include
}

Reading the constraints

Interview and contest problems tell you the target complexity through the input limit:

Limit on n Expected solution
n ≤ 20 Exponential is fine — backtracking, bitmask
n ≤ 5,000 O(n²)
n ≤ 10⁵ or 10⁶ O(n log n) or O(n)
n ≥ 10⁹ O(log n) or O(1) — maths, not iteration

Read the constraint first. It often names the algorithm before you have thought about the problem.