❤ Like
🔖 Save
🔗 Share
Eric Hu
Eric Hu

The Big O Notation

@thedevspaceio

⏱️ Big O Notation Cheatsheet

Big O describes how an algorithm scales with input size. This cheatsheet explains every complexity class with a growth diagram and code examples.

✅ O(1) Constant ✅ O(log n) Logarithmic ✅ O(n) Linear ✅ O(n log n) Linearithmic ✅ O(n²) Quadratic ✅ O(2ⁿ) Exponential ✅ O(n!) Factorial

#dsa #bigo #complexity #algorithms #datastructures #interview #coding #tips


Big O notation describes how an algorithm's running time or space grows as the input grows. It measures scalability, not exact speed.

Complexity classes

From fastest to slowest growth.

NotationNameExample
O(1)ConstantArray index access, hash map lookup
O(log n)LogarithmicBinary search
O(n)LinearSingle loop, array scan
O(n log n)LinearithmicMerge sort, quicksort (average)
O(n²)QuadraticNested loops, bubble sort
O(2ⁿ)ExponentialRecursive fibonacci, subsets
O(n!)FactorialPermutations, traveling salesman

O(1) — Constant

The running time stays the same no matter how large the input gets. The algorithm does a fixed amount of work.

O(1)


Array index access and hash map lookups are constant time.

js
const arr = [10, 20, 30];
arr[0]; // O(1) — direct index access
js
const map = new Map();
map.set("key", "value");
map.get("key"); // O(1) — hash lookup

Push and pop at the end of an array are also O(1).

js
arr.push(40); // O(1)
arr.pop(); // O(1)

O(log n) — Logarithmic

The running time grows by one step each time the input doubles. The algorithm eliminates half the remaining work each iteration.

O(log n)


Binary search is the classic example. It halves the search space each step.

js
function binarySearch(nums, target) {
  let left = 0;
  let right = nums.length - 1;
 
  while (left <= right) {
    const mid = (left + right) >> 1;
    if (nums[mid] === target) return mid;
    if (nums[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
 
  return -1;
}

Searching 1,000,000 sorted items takes at most 20 comparisons.


O(n) — Linear

The running time grows in direct proportion to the input. Double the input, double the work.

O(n)


A single loop over the input is linear.

js
function sum(nums) {
  let total = 0;
  for (const n of nums) total += n;
  return total;
}

Two sequential loops are still O(n). Drop the constant.

js
function twoLoops(nums) {
  for (const n of nums) console.log(n);
  for (const n of nums) console.log(n * 2);
}
// O(2n) → O(n)

O(n log n) — Linearithmic

A log n operation performed n times. This is the best possible for comparison-based sorting.

O(n log n)


Efficient sorts like merge sort and heap sort are O(n log n).

js
function mergeSort(nums) {
  if (nums.length <= 1) return nums;
 
  const mid = Math.floor(nums.length / 2);
  const left = mergeSort(nums.slice(0, mid)); // log n levels
  const right = mergeSort(nums.slice(mid));
 
  return merge(left, right); // O(n) merge per level
}

JavaScript's built-in Array.prototype.sort is also O(n log n).

js
[5, 2, 8, 1].sort((a, b) => a - b); // O(n log n)

O(n²) — Quadratic

The running time grows with the square of the input. Usually nested loops over the same data.

O(n²)


Nested loops comparing every pair are quadratic.

js
function hasDuplicate(nums) {
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] === nums[j]) return true;
    }
  }
  return false;
}

Bubble sort and selection sort are O(n²). Quadratic algorithms break around n = 10⁴.


O(2ⁿ) — Exponential

The running time doubles with each additional input element. Usually recursion with two branches and no memoization.

O(2ⁿ)


Naive recursive fibonacci recomputes the same values over and over.

js
// O(2ⁿ) — each call branches twice
function fib(n) {
  if (n <= 1) return n;
  return fib(n - 1) + fib(n - 2);
}

Generating all subsets of a set is also O(2ⁿ), since a set of n elements has 2ⁿ subsets.

js
function subsets(nums) {
  const result = [];
 
  function backtrack(start, current) {
    result.push([...current]);
    for (let i = start; i < nums.length; i++) {
      current.push(nums[i]);
      backtrack(i + 1, current);
      current.pop();
    }
  }
 
  backtrack(0, []);
  return result; // 2ⁿ subsets
}

Exponential algorithms become unusable around n = 30.


O(n!) — Factorial

The running time grows faster than exponential. Generating all permutations of n items is n!.

O(n!)


Generating all permutations is the classic example.

js
function permutations(nums) {
  const result = [];
 
  function backtrack(current, remaining) {
    if (remaining.length === 0) {
      result.push([...current]);
      return;
    }
    for (let i = 0; i < remaining.length; i++) {
      current.push(remaining[i]);
      backtrack(
        current,
        remaining.filter((_, j) => j !== i),
      );
      current.pop();
    }
  }
 
  backtrack([], nums);
  return result; // n! permutations
}

Factorial algorithms are only practical for very small inputs (n ≤ 10).


Growth comparison

How each class scales as input grows.

nO(1)O(log n)O(n)O(n log n)O(n²)O(2ⁿ)O(n!)
101310331001,0243,628,800
201420864001,048,5762.4 × 10¹⁸
3015301479001.1 × 10⁹2.7 × 10³²
4015402131,6001.1 × 10¹²8.2 × 10⁴⁷
5016502822,5001.1 × 10¹⁵3.0 × 10⁶⁴
6016603543,6001.2 × 10¹⁸8.3 × 10⁸¹
7016704304,9001.2 × 10²¹1.2 × 10¹⁰⁰
8016805066,4001.2 × 10²⁴7.2 × 10¹¹⁸
9016905848,1001.2 × 10²⁷1.5 × 10¹³⁸
1001710066410,0001.3 × 10³⁰9.3 × 10¹⁵⁷

Rule of thumb: 10⁸ operations per second. O(n²) breaks around n = 10⁴, O(n log n) handles n = 10⁶ easily.

Full-Stack AI Developer Roadmap

From HTML & CSS to working with AI models, all in one structured roadmap.

@thedevspaceio
www.thedevspace.io