📊

Big O Notation and Time Complexity

JavaScriptAdvanced

Analyze algorithm efficiency.

Code Example

JavaScript
// Big O Notation - How algorithm performance scales

// O(1) - Constant time
function getFirst(arr) {
  return arr[0];
}
// Always takes same time regardless of input size

// O(log n) - Logarithmic time
function binarySearch(arr, target) {
  let left = 0;
  let right = arr.length - 1;
  
  while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    if (arr[mid] === target) return mid;
    if (arr[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
  return -1;
}
// Halves search space each iteration

// O(n) - Linear time
function findMax(arr) {
  let max = arr[0];
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] > max) max = arr[i];
  }
  return max;
}
// Time grows linearly with input size

// O(n log n) - Linearithmic time
function mergeSort(arr) {
  if (arr.length <= 1) return arr;
  
  const mid = Math.floor(arr.length / 2);
  const left = mergeSort(arr.slice(0, mid));
  const right = mergeSort(arr.slice(mid));
  
  return merge(left, right);
}
// Common in efficient sorting algorithms

// O(n²) - Quadratic time
function bubbleSort(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr;
}
// Nested loops over same data

// O(2^n) - Exponential time
function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}
// Doubles with each addition to input

// O(n!) - Factorial time
function permutations(arr) {
  if (arr.length <= 1) return [arr];
  
  const result = [];
  arr.forEach((item, i) => {
    const rest = [...arr.slice(0, i), ...arr.slice(i + 1)];
    const restPerms = permutations(rest);
    restPerms.forEach(perm => result.push([item, ...perm]));
  });
  
  return result;
}
// Worst case - traveling salesman problem

// Space complexity examples

// O(1) space
function sum(arr) {
  let total = 0;
  for (let num of arr) total += num;
  return total;
}
// Only uses single variable

// O(n) space
function double(arr) {
  const result = [];
  for (let num of arr) result.push(num * 2);
  return result;
}
// Creates new array same size as input

// O(n²) space
function matrix(n) {
  const result = [];
  for (let i = 0; i < n; i++) {
    result[i] = [];
    for (let j = 0; j < n; j++) {
      result[i][j] = i * j;
    }
  }
  return result;
}
// Creates n×n matrix

// Practical examples

// Good: O(n)
function hasDuplicate(arr) {
  const seen = new Set();
  for (let item of arr) {
    if (seen.has(item)) return true;
    seen.add(item);
  }
  return false;
}

// Bad: O(n²)
function hasDuplicateBad(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;
}

// Amortized analysis
class DynamicArray {
  constructor() {
    this.array = [];
    this.capacity = 1;
    this.size = 0;
  }
  
  push(item) {
    if (this.size === this.capacity) {
      this.capacity *= 2;
      const newArray = new Array(this.capacity);
      for (let i = 0; i < this.size; i++) {
        newArray[i] = this.array[i];
      }
      this.array = newArray;
    }
    this.array[this.size++] = item;
  }
}
// Most pushes are O(1), occasional O(n) resize
// Amortized: O(1)

Explanation

Big O describes worst-case growth. O(1) constant, O(log n) logarithmic, O(n) linear, O(n log n) linearithmic, O(n²) quadratic, O(2^n) exponential. Space complexity measures memory usage.

Related Tutorials