Start Here

Big O Notation &
Complexity Analysis

Every solution you give in an interview, you'll be asked: "What's the time and space complexity?" This is the foundation of everything.

Complexity Hierarchy
CORE
From best โ†’ worstreference
O(1)        Constant     array[0], Map.get(), Set.has()
O(log n)    Logarithmic  Binary search, balanced BST
O(n)        Linear       Single loop, Array.find()
O(n log n)  Linearithmic Merge sort, Heap sort
O(nยฒ)       Quadratic    Nested loops, Bubble sort
O(2^n)      Exponential  Recursive subsets
O(n!)       Factorial    All permutations
Real numbers: n=1000 โ†’ O(1)=1 op, O(log n)=10, O(n)=1K, O(nยฒ)=1M, O(2^n)=astronomical. This is WHY choosing the right algorithm is life or death in production.
Rules for calculating Big OJavaScript
// Rule 1: Drop constants โ€” O(2n) = O(n)
for (const x of arr) doA(x);  // O(n)
for (const x of arr) doB(x);  // O(n)  โ†’ total: O(n), not O(2n)

// Rule 2: Drop non-dominant terms โ€” O(nยฒ + n) = O(nยฒ)

// Rule 3: Different inputs = different variables
// Loop over arr1 then arr2 โ†’ O(a + b), NOT O(n)
// Nested loops arr1 inside arr2 โ†’ O(a ร— b), NOT O(nยฒ)

// Space complexity: count extra memory you allocate
const result = [];            // O(n) space
const seen = new Set();       // O(n) space
let count = 0;               // O(1) space โ€” just a variable
Data Structure Complexity Cheatsheet
REFERENCE
All structures at a glancereference
Structure          Access  Search  Insert  Delete
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Array              O(1)    O(n)    O(n)    O(n)
Hash Map / Set     O(1)*   O(1)*   O(1)*   O(1)*
Linked List        O(n)    O(n)    O(1)**  O(1)**
Stack              O(n)    O(n)    O(1)    O(1)
Queue              O(n)    O(n)    O(1)    O(1)
BST (balanced)     O(log)  O(log)  O(log)  O(log)
BST (unbalanced)   O(n)    O(n)    O(n)    O(n)
Heap (Min/Max)     O(n)    O(n)    O(log)  O(log)
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
* amortized  ** with reference to node

Interview tip: When asked to optimize, think: "Can I use a HashMap to reduce search from O(n) to O(1)?"

Module 1

Arrays & Strings

The #1 most common interview topic. Master Two Pointers + Sliding Window and you'll handle 40% of array problems.

Two Pointers Pattern
PATTERN

When to use: Sorted array, pair-sum, palindrome check, removing duplicates in-place, merging sorted arrays.

Common Interview Questions
  • Two Sum (sorted input)
  • Valid Palindrome
  • Container With Most Water
  • Remove Duplicates from Sorted Array
  • 3Sum
Two Pointers โ€” examplesJavaScript
// Two Sum (sorted) โ€” O(n) time, O(1) space
function twoSum(nums, target) {
  let left = 0, right = nums.length - 1;
  while (left < right) {
    const sum = nums[left] + nums[right];
    if (sum === target) return [left, right];
    sum < target ? left++ : right--;
  }
}

// Valid Palindrome โ€” O(n) time, O(1) space
function isPalindrome(s) {
  s = s.toLowerCase().replace(/[^a-z0-9]/g, '');
  let l = 0, r = s.length - 1;
  while (l < r) if (s[l++] !== s[r--]) return false;
  return true;
}

// Container With Most Water โ€” O(n)
function maxArea(heights) {
  let l = 0, r = heights.length - 1, max = 0;
  while (l < r) {
    max = Math.max(max, Math.min(heights[l], heights[r]) * (r - l));
    heights[l] < heights[r] ? l++ : r--;
  }
  return max;
}
Sliding Window Pattern
PATTERN

When to use: "Longest/shortest subarray/substring satisfying X condition." Fixed or variable window size.

Common Interview Questions
  • Longest Substring Without Repeating Chars
  • Maximum Sum Subarray of Size K
  • Minimum Window Substring
  • Longest Repeating Character Replacement
Sliding Window โ€” examplesJavaScript
// Longest Substring Without Repeating Chars โ€” O(n)
function lengthOfLongestSubstring(s) {
  const seen = new Map();
  let max = 0, left = 0;
  for (let r = 0; r < s.length; r++) {
    if (seen.has(s[r]) && seen.get(s[r]) >= left)
      left = seen.get(s[r]) + 1;
    seen.set(s[r], r);
    max = Math.max(max, r - left + 1);
  }
  return max;
}

// Max Sum Subarray of Size K โ€” O(n)
function maxSumK(arr, k) {
  let sum = arr.slice(0, k).reduce((a,b) => a+b, 0);
  let max = sum;
  for (let i = k; i < arr.length; i++) {
    sum += arr[i] - arr[i - k];  // add right, remove left
    max = Math.max(max, sum);
  }
  return max;
}
HashMap / HashSet Pattern
PATTERN

The trick: Replace O(nยฒ) "have I seen this before?" nested loops with a HashMap for O(n) time + O(n) space tradeoff.

HashMap patternsJavaScript
// Two Sum (unsorted) โ€” O(n)
function twoSum(nums, target) {
  const map = new Map();
  for (let i = 0; i < nums.length; i++) {
    const comp = target - nums[i];
    if (map.has(comp)) return [map.get(comp), i];
    map.set(nums[i], i);
  }
}

// Valid Anagram โ€” frequency count
function isAnagram(s, t) {
  if (s.length !== t.length) return false;
  const freq = {};
  for (const c of s) freq[c] = (freq[c] ?? 0) + 1;
  for (const c of t) {
    if (!freq[c]) return false;
    freq[c]--;
  }
  return true;
}

// Group Anagrams โ€” sort key trick
function groupAnagrams(strs) {
  const map = new Map();
  for (const s of strs) {
    const key = [...s].sort().join('');
    if (!map.has(key)) map.set(key, []);
    map.get(key).push(s);
  }
  return [...map.values()];
}
Prefix Sum Pattern
PATTERN

When to use: "Sum of subarray from index i to j?" Build prefix array in O(n), answer each query in O(1).

Prefix SumJavaScript
// Build prefix sum โ€” O(n) setup, O(1) per query
function buildPrefix(arr) {
  const prefix = [0];
  for (const n of arr) prefix.push(prefix.at(-1) + n);
  return prefix;
}
// Sum from i to j: prefix[j+1] - prefix[i]

// Subarray Sum Equals K โ€” O(n)
function subarraySum(nums, k) {
  const counts = new Map([[0, 1]]);
  let sum = 0, result = 0;
  for (const n of nums) {
    sum += n;
    result += counts.get(sum - k) ?? 0;
    counts.set(sum, (counts.get(sum) ?? 0) + 1);
  }
  return result;
}
Module 2

Linked Lists

Pointer manipulation. The Fast & Slow pointer pattern solves most linked list interview problems.

Linked List Node & Basic Operations
STRUCTURE
Node definition + core opsJavaScript
class ListNode {
  constructor(val = 0, next = null) {
    this.val = val;
    this.next = next;
  }
}

// Build list: 1 โ†’ 2 โ†’ 3 โ†’ null
const head = new ListNode(1, new ListNode(2, new ListNode(3)));

// Traverse โ€” O(n)
let curr = head;
while (curr) { console.log(curr.val); curr = curr.next; }

// Reverse a linked list โ€” O(n) time, O(1) space
function reverse(head) {
  let prev = null, curr = head;
  while (curr) {
    const next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
  }
  return prev;  // new head
}
Fast & Slow Pointer (Floyd's Algorithm)
PATTERN
Imagine two runners on a circular track. The fast one runs at 2x speed. If there's a cycle, they'll eventually meet. If no cycle, fast reaches the end first.
Solves These Problems
  • Detect cycle in linked list
  • Find middle of linked list
  • Find start of cycle
  • Happy number (number theory!)
  • Palindrome linked list
Fast & Slow pointer patternsJavaScript
// Detect cycle โ€” O(n) time, O(1) space
function hasCycle(head) {
  let slow = head, fast = head;
  while (fast && fast.next) {
    slow = slow.next;
    fast = fast.next.next;
    if (slow === fast) return true;
  }
  return false;
}

// Find middle โ€” slow stops at middle when fast reaches end
function findMiddle(head) {
  let slow = head, fast = head;
  while (fast && fast.next) {
    slow = slow.next;
    fast = fast.next.next;
  }
  return slow;
}

// Merge two sorted lists โ€” O(n+m)
function mergeTwoLists(l1, l2) {
  const dummy = new ListNode(0);
  let curr = dummy;
  while (l1 && l2) {
    if (l1.val <= l2.val) { curr.next = l1; l1 = l1.next; }
    else { curr.next = l2; l2 = l2.next; }
    curr = curr.next;
  }
  curr.next = l1 || l2;
  return dummy.next;
}
Module 3

Stacks & Queues

Stack = LIFO (Last In First Out). Queue = FIFO (First In First Out). Monotonic stack is the secret weapon for many "next greater element" problems.

Stack โ€” In JavaScript
STACK
Like a stack of plates โ€” you can only add or remove from the top. Browser history (Back button), undo/redo, function call stack, expression parsing.
Stack operations + classic problemsJavaScript
// Stack uses array with push/pop โ€” both O(1)
const stack = [];
stack.push(1);      // add to top
stack.at(-1);       // peek top without removing
stack.pop();        // remove from top

// Valid Parentheses โ€” O(n)
function isValid(s) {
  const map = { ')':'(', '}':'{', ']':'[' };
  const stack = [];
  for (const ch of s) {
    if ('([{'.includes(ch)) stack.push(ch);
    else if (stack.pop() !== map[ch]) return false;
  }
  return stack.length === 0;
}

// Monotonic Decreasing Stack โ€” Next Greater Element
function nextGreater(nums) {
  const result = new Array(nums.length).fill(-1);
  const stack = [];  // stores indices
  for (let i = 0; i < nums.length; i++) {
    while (stack.length && nums[i] > nums[stack.at(-1)]) {
      result[stack.pop()] = nums[i];
    }
    stack.push(i);
  }
  return result;
}
Queue โ€” BFS Foundation
QUEUE
Like a line at a ticket counter โ€” first person in line gets served first. Used for BFS, task scheduling, and sliding window maximum.
Queue in JS (use shift or use index pointer)JavaScript
// โš ๏ธ Array.shift() is O(n) โ€” use index pointer for O(1)
class Queue {
  constructor() { this.data = []; this.head = 0; }
  enqueue(val) { this.data.push(val); }
  dequeue() { return this.data[this.head++]; }
  peek()    { return this.data[this.head]; }
  isEmpty() { return this.head >= this.data.length; }
}

// Implement Stack using two Queues (classic interview trick)
// Implement Queue using two Stacks (classic interview trick)
class MyQueue {
  constructor() { this.inbox = []; this.outbox = []; }
  push(x)  { this.inbox.push(x); }
  peek() {
    if (!this.outbox.length)
      while (this.inbox.length) this.outbox.push(this.inbox.pop());
    return this.outbox.at(-1);
  }
  pop()    { this.peek(); return this.outbox.pop(); }
  empty()  { return !this.inbox.length && !this.outbox.length; }
}
Module 4

Binary Trees & BST

Trees appear in ~30% of interviews. The DFS recursive pattern solves most problems. Learn it cold.

Tree Traversals โ€” DFS & BFS
TRAVERSAL
All 4 traversalsJavaScript
class TreeNode {
  constructor(val, left=null, right=null) {
    this.val = val; this.left = left; this.right = right;
  }
}

// Inorder: left โ†’ root โ†’ right (gives SORTED output for BST!)
function inorder(node, result=[]) {
  if (!node) return result;
  inorder(node.left, result);
  result.push(node.val);
  inorder(node.right, result);
  return result;
}

// Preorder: root โ†’ left โ†’ right (used for copying tree)
// Postorder: left โ†’ right โ†’ root (used for deleting tree)

// BFS โ€” Level Order (uses Queue)
function levelOrder(root) {
  if (!root) return [];
  const result = [], queue = [root];
  while (queue.length) {
    const level = [];
    const size = queue.length;  // snapshot size for this level
    for (let i = 0; i < size; i++) {
      const node = queue.shift();
      level.push(node.val);
      if (node.left) queue.push(node.left);
      if (node.right) queue.push(node.right);
    }
    result.push(level);
  }
  return result;
}
Classic Tree Problems
PROBLEMS
Must-know tree algorithmsJavaScript
// Max Depth โ€” O(n)
const maxDepth = root =>
  root ? 1 + Math.max(maxDepth(root.left), maxDepth(root.right)) : 0;

// Check Balanced โ€” O(n)
function isBalanced(root) {
  function height(node) {
    if (!node) return 0;
    const l = height(node.left);
    if (l === -1) return -1;
    const r = height(node.right);
    if (r === -1 || Math.abs(l - r) > 1) return -1;
    return 1 + Math.max(l, r);
  }
  return height(root) !== -1;
}

// Lowest Common Ancestor โ€” O(n)
function lowestCommonAncestor(root, p, q) {
  if (!root || root === p || root === q) return root;
  const left  = lowestCommonAncestor(root.left, p, q);
  const right = lowestCommonAncestor(root.right, p, q);
  return !left ? right : !right ? left : root;
}

// Validate BST โ€” O(n)
function isValidBST(node, min=-Infinity, max=Infinity) {
  if (!node) return true;
  if (node.val <= min || node.val >= max) return false;
  return isValidBST(node.left, min, node.val) &&
         isValidBST(node.right, node.val, max);
}
Module 5

Heaps & Priority Queue

A heap gives you the min or max element in O(1), insertion/deletion in O(log n). Critical for "Top K" problems.

Min Heap in JavaScript
HEAP
JS has no built-in heap! In interviews you either implement one or use a sorted array as approximation. In real code, use a library like 'heap-js'.
MinHeap implementationJavaScript
class MinHeap {
  constructor() { this.heap = []; }

  push(val) {
    this.heap.push(val);
    this._bubbleUp(this.heap.length - 1);
  }

  pop() {
    const min = this.heap[0];
    const last = this.heap.pop();
    if (this.heap.length) { this.heap[0] = last; this._sinkDown(0); }
    return min;
  }

  peek() { return this.heap[0]; }
  size() { return this.heap.length; }

  _bubbleUp(i) {
    while (i > 0) {
      const parent = Math.floor((i - 1) / 2);
      if (this.heap[parent] <= this.heap[i]) break;
      [this.heap[i], this.heap[parent]] = [this.heap[parent], this.heap[i]];
      i = parent;
    }
  }

  _sinkDown(i) {
    const n = this.heap.length;
    while (true) {
      let min = i, l = 2*i+1, r = 2*i+2;
      if (l < n && this.heap[l] < this.heap[min]) min = l;
      if (r < n && this.heap[r] < this.heap[min]) min = r;
      if (min === i) break;
      [this.heap[i], this.heap[min]] = [this.heap[min], this.heap[i]];
      i = min;
    }
  }
}

// Top K Frequent Elements โ€” O(n log k)
function topKFrequent(nums, k) {
  const freq = new Map();
  for (const n of nums) freq.set(n, (freq.get(n)??0)+1);
  // Sort approach for interviews without MinHeap available:
  return [...freq.entries()]
    .sort((a, b) => b[1] - a[1])
    .slice(0, k).map(e => e[0]);
}
Module 6

Graphs โ€” BFS & DFS

Graphs are trees without the constraints. BFS finds shortest path. DFS explores all possibilities. Both use a "visited" set to avoid infinite loops.

Graph Representations
STRUCTURE
Adjacency list โ€” most common in interviewsJavaScript
// Adjacency List โ€” O(V+E) space (preferred)
const graph = {
  A: ['B', 'C'],
  B: ['A', 'D'],
  C: ['A'],
  D: ['B']
};

// Build from edge list
function buildGraph(edges) {
  const adj = new Map();
  for (const [u, v] of edges) {
    if (!adj.has(u)) adj.set(u, []);
    if (!adj.has(v)) adj.set(v, []);
    adj.get(u).push(v);
    adj.get(v).push(u);  // remove for directed graph
  }
  return adj;
}
BFS โ€” Shortest Path
BFS
Flood fill from the source, level by level. The first time you reach a node, that's the shortest path. Like ripples spreading across water.
BFS templateJavaScript
function bfs(graph, start, target) {
  const visited = new Set([start]);
  const queue = [[start, 0]];  // [node, distance]

  while (queue.length) {
    const [node, dist] = queue.shift();
    if (node === target) return dist;

    for (const neighbor of (graph[node] ?? [])) {
      if (!visited.has(neighbor)) {
        visited.add(neighbor);
        queue.push([neighbor, dist + 1]);
      }
    }
  }
  return -1;  // not reachable
}

// Number of Islands โ€” BFS on 2D grid
function numIslands(grid) {
  let count = 0;
  for (let r = 0; r < grid.length; r++) {
    for (let c = 0; c < grid[0].length; c++) {
      if (grid[r][c] === '1') { bfsIsland(grid, r, c); count++; }
    }
  }
  return count;
}
function bfsIsland(grid, r, c) {
  const dirs = [[0,1],[0,-1],[1,0],[-1,0]];
  const q = [[r,c]]; grid[r][c] = '0';  // mark visited
  while (q.length) {
    const [row, col] = q.shift();
    for (const [dr,dc] of dirs) {
      const nr = row+dr, nc = col+dc;
      if (nr>=0&&nr=0&&nc0].length&&grid[nr][nc]==='1') {
        grid[nr][nc] = '0'; q.push([nr,nc]);
      }
    }
  }
}
DFS โ€” Explore All Paths
DFS
DFS template + topological sortJavaScript
// DFS recursive template
function dfs(graph, node, visited = new Set()) {
  if (visited.has(node)) return;
  visited.add(node);
  // process node here
  for (const neighbor of (graph[node] ?? [])) {
    dfs(graph, neighbor, visited);
  }
}

// Detect cycle in directed graph
function hasCycle(graph, n) {
  const WHITE=0, GRAY=1, BLACK=2;
  const color = new Array(n).fill(WHITE);
  function dfs(node) {
    color[node] = GRAY;  // currently visiting
    for (const nb of graph[node]) {
      if (color[nb] === GRAY) return true;  // back edge = cycle
      if (color[nb] === WHITE && dfs(nb)) return true;
    }
    color[node] = BLACK;  // fully processed
    return false;
  }
  return [...Array(n).keys()].some(i => color[i]===WHITE && dfs(i));
}
Module 7

Sorting Algorithms

You'll rarely implement these from scratch, but you WILL be asked to explain them and their complexities. Know Merge Sort and Quick Sort deeply.

Sorting Complexity Overview
REFERENCE
All sorting algorithmsreference
Algorithm      Best       Average    Worst      Space  Stable
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Bubble Sort    O(n)       O(nยฒ)      O(nยฒ)      O(1)   Yes
Selection Sort O(nยฒ)      O(nยฒ)      O(nยฒ)      O(1)   No
Insertion Sort O(n)       O(nยฒ)      O(nยฒ)      O(1)   Yes
Merge Sort     O(n log n) O(n log n) O(n log n) O(n)   Yes
Quick Sort     O(n log n) O(n log n) O(nยฒ)      O(log) No
Heap Sort      O(n log n) O(n log n) O(n log n) O(1)   No
Counting Sort  O(n+k)     O(n+k)     O(n+k)     O(k)   Yes
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
JS Array.sort() uses TimSort โ†’ O(n log n) stable
Merge Sort โ€” Divide & Conquer
ALGORITHM
Split the array in half, sort each half, then merge them back together sorted. Like sorting a huge pile of papers by splitting into smaller piles, sorting each, then interleaving them back together.
Merge Sort โ€” O(n log n)JavaScript
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);
}

function merge(left, right) {
  const result = [];
  let l = 0, r = 0;
  while (l < left.length && r < right.length) {
    result.push(left[l] <= right[r] ? left[l++] : right[r++]);
  }
  return [...result, ...left.slice(l), ...right.slice(r)];
}
// Time: O(n log n) guaranteed | Space: O(n)
// Stable: Yes โ€” equal elements keep original order
Quick Sort โ€” In-place
ALGORITHM
Quick Sort โ€” avg O(n log n)JavaScript
function quickSort(arr, lo=0, hi=arr.length-1) {
  if (lo >= hi) return;
  const pivot = partition(arr, lo, hi);
  quickSort(arr, lo, pivot - 1);
  quickSort(arr, pivot + 1, hi);
}

function partition(arr, lo, hi) {
  const pivot = arr[hi];  // last element as pivot
  let i = lo;
  for (let j = lo; j < hi; j++) {
    if (arr[j] <= pivot) {
      [arr[i], arr[j]] = [arr[j], arr[i]]; i++;
    }
  }
  [arr[i], arr[hi]] = [arr[hi], arr[i]];
  return i;
}
// Avg: O(n log n) | Worst: O(nยฒ) if already sorted + bad pivot
// Space: O(log n) recursion stack | Not stable
Module 8

Binary Search

Reduces search from O(n) to O(log n) on sorted data. More powerful than most devs realize โ€” it applies to any "monotonic" problem, not just sorted arrays.

Binary Search โ€” 3 Templates
ALGORITHM
Binary search variantsJavaScript
// Template 1: Find exact target
function binarySearch(arr, target) {
  let lo = 0, hi = arr.length - 1;
  while (lo <= hi) {
    const mid = lo + Math.floor((hi - lo) / 2);  // avoid overflow
    if (arr[mid] === target) return mid;
    arr[mid] < target ? lo = mid + 1 : hi = mid - 1;
  }
  return -1;
}

// Template 2: Find leftmost position (first occurrence)
function leftBound(arr, target) {
  let lo = 0, hi = arr.length;
  while (lo < hi) {
    const mid = lo + Math.floor((hi - lo) / 2);
    arr[mid] < target ? lo = mid + 1 : hi = mid;
  }
  return lo;  // insertion point
}

// Binary Search on answer โ€” "Is it possible to do X with Y?"
// Koko eating bananas, split array with minimum largest sum, etc.
function minCapacity(piles, hours) {
  let lo = 1, hi = Math.max(...piles);
  while (lo < hi) {
    const mid = lo + Math.floor((hi - lo) / 2);
    canFinish(piles, hours, mid) ? hi = mid : lo = mid + 1;
  }
  return lo;
}
function canFinish(piles, hours, speed) {
  return piles.reduce((h, p) => h + Math.ceil(p/speed), 0) <= hours;
}
Module 9

Dynamic Programming

The hardest and most rewarding interview topic. DP = "solve subproblems, cache results, avoid recomputation." Once you see the pattern, it clicks everywhere.

DP Framework โ€” 3 Steps
FRAMEWORK
Fibonacci without DP: fib(5) calls fib(4) and fib(3). fib(4) calls fib(3) again. fib(3) is computed many times. With memoization: compute it once, store it, reuse it. Exponential โ†’ linear.
Top-down (Memoization) vs Bottom-up (Tabulation)JavaScript
// Fibonacci โ€” 3 approaches

// Brute force: O(2^n)
const fib1 = n => n <= 1 ? n : fib1(n-1) + fib1(n-2);

// Top-down Memoization: O(n) time + O(n) space
function fib2(n, memo={}) {
  if (n <= 1) return n;
  if (memo[n]) return memo[n];
  memo[n] = fib2(n-1, memo) + fib2(n-2, memo);
  return memo[n];
}

// Bottom-up Tabulation: O(n) time + O(n) space
function fib3(n) {
  const dp = [0, 1];
  for (let i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2];
  return dp[n];
}

// Space-optimized: O(n) time + O(1) space
function fib4(n) {
  let a = 0, b = 1;
  for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
  return b;
}
Classic DP Problems
PROBLEMS
Coin Change, Climb Stairs, House RobberJavaScript
// Climbing Stairs (distinct ways to climb n stairs, 1 or 2 steps)
function climbStairs(n) {
  let [a, b] = [1, 1];
  for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
  return b;  // same as fibonacci!
}

// Coin Change (fewest coins to make amount)
function coinChange(coins, amount) {
  const dp = new Array(amount + 1).fill(Infinity);
  dp[0] = 0;
  for (let i = 1; i <= amount; i++) {
    for (const coin of coins) {
      if (coin <= i) dp[i] = Math.min(dp[i], dp[i - coin] + 1);
    }
  }
  return dp[amount] === Infinity ? -1 : dp[amount];
}

// House Robber (max rob without adjacent houses)
function rob(nums) {
  let [prev2, prev1] = [0, 0];
  for (const n of nums) {
    [prev2, prev1] = [prev1, Math.max(prev1, prev2 + n)];
  }
  return prev1;
}

// Longest Common Subsequence โ€” 2D DP
function longestCommonSubsequence(text1, text2) {
  const m = text1.length, n = text2.length;
  const dp = Array.from({length:m+1},()=>new Array(n+1).fill(0));
  for (let i=1; i<=m; i++)
    for (let j=1; j<=n; j++)
      dp[i][j] = text1[i-1]===text2[j-1]
        ? dp[i-1][j-1]+1
        : Math.max(dp[i-1][j], dp[i][j-1]);
  return dp[m][n];
}
Cheatsheet

Top Interview Patterns

Given a problem, how do you know WHICH pattern to apply? This is the meta-skill that separates good candidates from great ones.

Pattern Recognition Guide โ€” "How Do I Know Which Pattern to Use?"
META
Problem clues โ†’ Pattern to usedecision tree
CLUE IN PROBLEM                      โ†’ USE THIS PATTERN
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
"sorted array" + "pair/triplet"      โ†’ Two Pointers
"subarray/substring" + "longest/shortest/exactly K" โ†’ Sliding Window
"have I seen this before?"           โ†’ HashMap / HashSet
"sum of subarray from i to j"        โ†’ Prefix Sum
"top K / K largest / K most frequent" โ†’ Heap (Min/Max)
"shortest path" / "minimum steps"   โ†’ BFS (unweighted graph)
"all paths" / "any path"             โ†’ DFS / Backtracking
"tree problem" (most)                โ†’ DFS Recursion
"tree level by level"                โ†’ BFS
"sorted data" + "search"             โ†’ Binary Search
"sorted data" + "O(nยฒ) feels wrong"  โ†’ Binary Search on answer
"overlapping subproblems"            โ†’ Dynamic Programming
"permutations/combinations/subsets"  โ†’ Backtracking (DFS)
"interval overlap"                   โ†’ Sort by start, greedy
"next greater/smaller element"       โ†’ Monotonic Stack
"connected components"               โ†’ Union Find / DFS
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Backtracking Template
PATTERN

Systematically explore all possibilities by making choices, recursing, and undoing choices (backtracking) when they don't work.

Subsets + PermutationsJavaScript
// All subsets โ€” O(2^n)
function subsets(nums) {
  const result = [];
  function backtrack(start, current) {
    result.push([...current]);
    for (let i = start; i < nums.length; i++) {
      current.push(nums[i]);      // choose
      backtrack(i + 1, current); // explore
      current.pop();              // undo (backtrack)
    }
  }
  backtrack(0, []);
  return result;
}

// All permutations โ€” O(n!)
function permute(nums) {
  const result = [];
  function backtrack(current, remaining) {
    if (!remaining.length) { result.push([...current]); return; }
    for (let i = 0; i < remaining.length; i++) {
      current.push(remaining[i]);
      backtrack(current, [...remaining.slice(0,i),...remaining.slice(i+1)]);
      current.pop();
    }
  }
  backtrack([], nums);
  return result;
}
Interview Communication Script
SOFT SKILL

The code is only half the interview. HOW you communicate is equally important.

What to say at each stepscript
1. UNDERSTAND: Repeat the problem back.
   "So we need to find... and the output should be...
    Can input be negative? What if array is empty?"

2. EXAMPLES: Walk through given examples + edge cases.
   "For input [2,7,11,15] target 9 โ†’ output [0,1]
    Edge case: what if no pair exists?"

3. BRUTE FORCE: Always mention it first.
   "Naive approach: nested loops, O(nยฒ) time.
    But we can do better..."

4. OPTIMIZE: State the pattern you'll use.
   "I'll use a HashMap to reduce to O(n) time, O(n) space."

5. CODE: Talk through what you're writing.
   "I'll initialize a map, then loop through..."

6. VERIFY: Trace through your code with example.
   "For input [2,7,11,15]: i=0, comp=7, not in map...
    i=1, comp=2, found! Return [0,1] โœ“"

7. COMPLEXITY: Always state this at the end.
   "Time: O(n), Space: O(n)"