ANALYSIS & FOUNDATIONS / ALGORITHM BRIEF

Big O notation

Big O notation describes how the running time or memory of an algorithm grows as the input size n grows.

BeginnerPhase 01 / Topic 1 of 7Mental modelComplexityEdge cases
01

Overview

Big O notation describes how the running time or memory of an algorithm grows as the input size n grows. It ignores machine speed and constant factors, and keeps only the term that dominates for large n. An algorithm that does 3n + 20 steps is O(n); one that does n^2 / 2 + 5n steps is O(n^2).

Big O lets you compare solutions before writing them and predict whether they will pass. With n = 100,000, an O(n^2) solution does about 10 billion operations and will time out, while O(n log n) does about 1.7 million and finishes instantly. Reading constraints and choosing the right target complexity is the first skill of problem solving.

Delivering letters in a building

Handing one letter to the doorman is O(1): it takes the same time no matter how many flats exist. Walking to every flat once is O(n). Asking every resident to greet every other resident is O(n^2). As the building grows from 10 to 1,000 flats, the first stays instant, the second takes 100x longer, and the third takes 10,000x longer.

02

When to use it

  • Comparing two approaches to the same problem before coding.
  • Reading problem constraints to decide which complexity will pass (see the table below).
  • Explaining in an interview why your optimized solution is better than brute force.
  • Predicting how a production endpoint behaves when data grows 10x or 100x.
03

Problem patterns it solves

Constraints reveal the target complexity

Recognize it when: n <= 10 means O(n!) or O(2^n) is fine; n <= 20 means O(2^n); n <= 500 means O(n^3); n <= 5,000 means O(n^2); n <= 10^6 means O(n) or O(n log n); n up to 10^18 means O(log n) or O(1).

  • Any LeetCode problem: read the Constraints section first
  • 1. Two Sum (n <= 10^4: O(n^2) passes, O(n) is expected)
  • 4. Median of Two Sorted Arrays (asks explicitly for O(log(m + n)))
Code shape tells you the complexity

Recognize it when: one loop over n is O(n); two nested loops is O(n^2); a loop that halves n is O(log n); sorting is O(n log n); recursion with 2 branches and depth n is O(2^n).

  • 509. Fibonacci Number (naive recursion is O(2^n))
  • 704. Binary Search (halving is O(log n))
  • 912. Sort an Array (O(n log n))
Trade memory for time

Recognize it when: a nested search loop can often be replaced by a hash map lookup, turning O(n^2) into O(n) with O(n) extra space.

  • 1. Two Sum
  • 217. Contains Duplicate
  • 128. Longest Consecutive Sequence
04

Where it is used in real software

API performance reviews

An endpoint that loops over all orders for every user is O(users x orders). It works in development with 50 rows and fails in production with millions. Big O spots this before deployment.

Database query planning

Databases choose between a full table scan O(n) and an index lookup O(log n). EXPLAIN plans in PostgreSQL show which one was chosen and why.

Frontend rendering

Rendering a list with a nested .find() inside .map() is O(n^2). Building a Map by id first makes it O(n) and removes visible lag on large tables.

Capacity planning

Knowing a batch job is O(n log n) lets teams estimate how long it will take when data doubles, and whether it still fits in the nightly window.

05

Key terms

n
The size of the input: array length, number of nodes, string length, or the numeric value itself.
Big O (upper bound)
Growth is at most proportional to f(n) for large n. Used for worst-case guarantees.
Big Omega (lower bound)
Growth is at least proportional to f(n). Example: comparison sorting is Omega(n log n).
Big Theta (tight bound)
Growth is exactly proportional to f(n), both upper and lower bound.
Dominant term
The fastest-growing term. In n^2 + 100n + 7, n^2 dominates for large n.
Amortized
Average cost per operation over a long sequence, even if some single operations are expensive.
06

How to find the Big O of any code

  1. 1
    Define n

    Decide what grows: the array length, the number of nodes, or both (use n and m separately for two inputs).

  2. 2
    Count the work of each block

    A simple statement is O(1). A loop that runs n times multiplies the work of its body by n.

  3. 3
    Multiply for nesting, add for sequence

    A loop inside a loop is O(n x n) = O(n^2). Two separate loops one after the other are O(n + n) = O(n).

  4. 4
    Watch for hidden loops

    Array.includes, indexOf, slice, splice, string concatenation in a loop, and list.remove(0) are all O(n) themselves.

  5. 5
    Drop constants and smaller terms

    O(2n) becomes O(n); O(n^2 + n) becomes O(n^2); O(n / 2) becomes O(n).

  6. 6
    Keep independent inputs separate

    Looping over array a then array b is O(a + b), not O(n). Nested over both is O(a x b).

07

How fast each complexity grows

Approximate operation counts. A typical judge runs about 10^8 simple operations per second.

Step 1 / 5
nO(log n)O(n)O(n log n)O(n^2)O(2^n)
10310331001,024
100710066410,0001.3 x 10^30
1,000101,0009,9661,000,000too large
100,00017100,0001.7 million10 billion (TLE)too large
1,000,000201 million20 million10^12 (TLE)too large

NOWn: 10 | O(log n): 3 | O(n): 10 | O(n log n): 33 | O(n^2): 100 | O(2^n): 1,024

O(log n) barely moves, O(n) and O(n log n) stay practical up to millions, O(n^2) becomes too slow around 10^5, and O(2^n) is only usable for n around 20 to 25.

08

Implementation

// O(1): same work regardless of nfunction first(arr) {  return arr[0];} // O(n): touches each element oncefunction sum(arr) {  let total = 0;  for (const x of arr) total += x;  return total;} // O(n^2): every pairfunction hasDuplicateSlow(arr) {  for (let i = 0; i < arr.length; i++) {    for (let j = i + 1; j < arr.length; j++) {      if (arr[i] === arr[j]) return true;    }  }  return false;} // O(n) time, O(n) space: trade memory for speedfunction hasDuplicateFast(arr) {  const seen = new Set();  for (const x of arr) {    if (seen.has(x)) return true;    seen.add(x);  }  return false;} // O(log n): the range halves every iterationfunction countHalvings(n) {  let steps = 0;  while (n > 1) {    n = Math.floor(n / 2);    steps++;  }  return steps;} // Hidden O(n^2): includes() is itself a loopfunction uniqueSlow(arr) {  const out = [];  for (const x of arr) if (!out.includes(x)) out.push(x);  return out;}
09

Complexity and performance

O(1)Constant

Array index, hash map get/put (average), push/pop on a stack.

O(log n)Logarithmic

Binary search, balanced tree operations, heap push/pop.

O(n)Linear

Single pass, linear search, building a hash map.

O(n log n)Linearithmic

Efficient sorting (merge sort, heap sort, Timsort).

O(n^2)Quadratic

All pairs, nested loops, bubble/insertion sort worst case.

O(2^n) / O(n!)Exponential

All subsets / all permutations. Only for tiny n.

10

Trade-offs

Big O hides constants

An O(n) algorithm with a huge constant can be slower than O(n log n) for realistic n. Insertion sort beats merge sort for arrays under about 16 elements, which is why Timsort uses it for small runs.

Worst case vs average case

Quicksort is O(n^2) in the worst case but O(n log n) on average. Hash maps are O(1) average but O(n) worst case under heavy collisions.

Time vs space

Faster solutions often use more memory (hash maps, memo tables). Check the memory limit as well as the time limit.

11

Variants and related techniques

Multiple variables

Graphs are O(V + E); a grid is O(rows x cols); comparing two strings is O(n x m). Never collapse different inputs into one n.

Output-sensitive complexity

Generating all subsets is O(2^n x n) because the output itself has that size; no algorithm can be faster than the size of its output.

Pseudo-polynomial

Knapsack DP is O(n x W). It looks polynomial, but W is a number value, so it can be huge relative to the input's length in bits.

12

Common mistakes

  • Calling an O(n) built-in inside a loop.

    Fix: Know the cost of includes, indexOf, splice, shift, substring, and list.remove(index). Use a Set or Map for repeated lookups.

  • Saying O(2n) or O(n + 5).

    Fix: Drop constants: both are O(n).

  • Adding when you should multiply.

    Fix: Nested loops multiply; sequential loops add.

  • Ignoring string building cost.

    Fix: Repeated s += c in a loop can be O(n^2) in Java. Use StringBuilder, or collect parts in a JS array and join once.

13

Interview questions

What is the difference between O, Omega, and Theta?

O is an upper bound (grows no faster than), Omega is a lower bound (grows no slower than), and Theta is a tight bound (both). In interviews, people say Big O but usually mean the tight worst-case bound.

Why do we drop constants?

Big O describes growth rate as n becomes large. Constants depend on hardware and implementation; the growth rate decides whether an algorithm scales.

Is O(n log n) always better than O(n^2)?

For large n, yes. For very small n, constants matter, and a simple O(n^2) algorithm can be faster. That is why hybrid sorts switch to insertion sort for small ranges.

Given n <= 10^5, which complexities pass?

O(n), O(n log n), and O(n sqrt n) are safe. O(n^2) is 10^10 operations and will time out.

14

Practice problems

ProblemDifficultyWhat it trains
Analyze 10 of your past solutionsEasyState time and space for each, including hidden built-in costs.
217. Contains DuplicateEasyCompare O(n^2), O(n log n), and O(n) approaches.
1. Two SumEasyO(n^2) brute force to O(n) with a hash map.
509. Fibonacci NumberEasyO(2^n) recursion to O(n) DP to O(1) space.
15. 3SumMediumO(n^3) to O(n^2) with sorting and two pointers.