Prep Guide / Big-O

Big-O & Time Complexity: A Practical Primer

What Big-O actually measures, the classes you will meet in interviews, and how to analyze your own code.

What Big-O measures

Big-O notation describes how the running time or memory of an algorithm grows as the input size n grows, ignoring constant factors and lower-order terms. It captures the shape of growth, not a stopwatch reading: an O(n) algorithm may be slower than an O(n²) one on tiny inputs, but as n increases the O(n) version wins, and eventually wins by a landslide.

Interviewers ask for complexity because it predicts behavior at scale. Code that is fine on 100 items can hang on 100,000 if it is quadratic. Stating the complexity also shows you understand the cost of your approach, not just that it produces the right answer.

The complexity classes, fastest to slowest

NotationNameTypical example
O(1)ConstantHash-map lookup, array index
O(log n)LogarithmicBinary search
O(n)LinearOne pass over an array
O(n log n)LinearithmicEfficient sorting (merge / heap sort)
O(n²)QuadraticNested loops over the same array
O(2ⁿ)ExponentialGenerating all subsets
O(n!)FactorialGenerating all permutations

As a rule of thumb for a 1-second limit: O(n) and O(n log n) comfortably handle millions of elements, O(n²) is fine into the low thousands, and anything exponential is only viable for small n (roughly n ≤ 20).

How to analyze a loop

Count how many times the innermost work runs as a function of n. A single pass is linear; a loop nested inside another over the same data is quadratic; a loop that halves the remaining work each step is logarithmic.

// O(n): each element once
for (int x : nums) total += x;

// O(n^2): every pair
for (int i = 0; i < n; i++)
    for (int j = i + 1; j < n; j++)
        consider(nums[i], nums[j]);

// O(log n): halve the range each step
while (lo < hi) { int mid = (lo + hi) / 2; ... }

Analyzing recursion

Picture the recursion as a tree and add up the work across all nodes. Merge sort splits the input in two and does O(n) work merging at each of its O(log n) levels, giving O(n log n). Naive Fibonacci branches into two calls that mostly recompute the same values, producing an O(2ⁿ) explosion — the classic signal that memoization or dynamic programming is needed.

Space complexity

Space complexity counts the extra memory an algorithm uses beyond its input. The recursion call stack counts: a depth-first traversal of a tree of height h uses O(h) stack space even though it allocates no data structures. A common trade-off is spending O(n) space (say, a hash set) to bring time down from O(n²) to O(n).

Worst, average, and amortized

Some structures have different costs depending on the case. A hash map is O(1) on average but O(n) in a pathological worst case; quicksort averages O(n log n) but degrades to O(n²) on bad pivots. "Amortized" describes the average cost per operation across a sequence: appending to a dynamic array is O(1) amortized, even though the occasional resize is O(n), because resizes are rare enough to average out.

What interviewers want to hear

Once the notation feels natural, every pattern on the patterns page comes with its expected complexity, and you can sanity-check your solutions as you practice.

← Back to the prep guide