Each pattern leans on the one before it — hashing makes more sense once you've felt array brute-force pain, and DP is just recursion once trees have made recursion click.
STAGE 00
Big-O & how to actually think about complexity
Week 1Most people memorize “O(n) good, O(n²) bad” without being able to derive complexity from real code — which means they can't debug a slow function or make an honest tradeoff in an interview.
What you'll learn
- Deriving time and space complexity directly from nested loops and recursion
- Best, worst, and average case — and why interviewers usually mean worst case
- Why constant factors get dropped in Big-O, but still matter in the real world
Code
// O(n) — one pass
function linearSearch(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) return i;
}
return -1;
}
// O(log n) — halves the search space each step
function binarySearch(sortedArr, target) {
let lo = 0, hi = sortedArr.length - 1;
while (lo <= hi) {
const mid = Math.floor((lo + hi) / 2);
if (sortedArr[mid] === target) return mid;
if (sortedArr[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;
}
// Binary search on 1,000,000 items: ~20 comparisons, not 1,000,000BuildTake 5 real code snippets and write down their Big-O before checking — nested loops, a loop with a break, a recursive function, an array method chain.
Self-checkWhat's the time complexity of a loop inside a loop that both run n times, but the inner one breaks early half the time on average?
STAGE 01
Arrays & two pointers
Week 1–2Two-pointer is the single pattern that shows up in more interview problems than almost any other — but it's usually taught as “just try it” instead of a repeatable recipe.
What you'll learn
- The two-pointer technique: opposite ends closing in, or same-direction fast/slow
- Sliding window as two-pointer's cousin — a window that grows and shrinks
- In-place array manipulation without extra memory
Code
// Two Sum on a SORTED array — O(n), no extra space
function twoSumSorted(arr, target) {
let left = 0, right = arr.length - 1;
while (left < right) {
const sum = arr[left] + arr[right];
if (sum === target) return [left, right];
if (sum < target) left++; // need a bigger sum — move left up
else right--; // need a smaller sum — move right down
}
return null;
}BuildImplement “remove duplicates from a sorted array in-place” and “container with most water” using two pointers.
Self-checkWhy does the two-pointer technique only work reliably on sorted (or otherwise ordered) data?
STAGE 02
Hashing & the space-time tradeoff
Week 2Hash maps turn O(n²) brute-force problems into O(n) by trading memory for speed. Recognizing WHEN to make that trade — not just how a hash map works — is the actual interview skill.
What you'll learn
- Hash map/set operations and real average-case O(1) lookup
- Collision handling basics, and why worst case isn't actually O(1)
- When hashing beats sorting for a given problem
Code
// Brute force Two Sum — O(n²)
function twoSumBrute(arr, target) {
for (let i = 0; i < arr.length; i++)
for (let j = i + 1; j < arr.length; j++)
if (arr[i] + arr[j] === target) return [i, j];
}
// Hash map Two Sum — O(n), one pass
function twoSumHash(arr, target) {
const seen = new Map();
for (let i = 0; i < arr.length; i++) {
const complement = target - arr[i];
if (seen.has(complement)) return [seen.get(complement), i];
seen.set(arr[i], i);
}
}BuildSolve “group anagrams” and “first non-repeating character in a string” using a hash map.
Self-checkWhy is average-case hash map lookup O(1) but worst-case O(n)? When would you actually hit that worst case?
STAGE 03
Linked lists
Week 3Linked lists force you to think in pointers/references instead of array indices — exactly the mental shift you need before trees and graphs make sense.
What you'll learn
- Singly vs. doubly linked lists, and what each pointer actually stores
- Fast/slow pointer (Floyd's algorithm) for cycle detection
- Reversing a linked list in-place, iteratively
Code
// Reverse a singly linked list in-place — O(n), O(1) space
function reverseList(head) {
let prev = null, curr = head;
while (curr) {
const next = curr.next; // save before we overwrite it
curr.next = prev; // reverse the pointer
prev = curr;
curr = next;
}
return prev; // prev is now the new head
}BuildDetect a cycle in a linked list, and find its middle node — both in a single pass, both with the fast/slow pointer.
Self-checkWhy does the fast/slow pointer technique guarantee finding a cycle if one exists?
STAGE 04
Stacks & queues
Week 3–4Stacks and queues aren't just data structures to memorize — they're the actual mechanism behind recursion, undo/redo, browser history, and BFS.
What you'll learn
- LIFO vs. FIFO, and matching each to the problems that need them
- Using a stack for bracket matching and the monotonic stack pattern
- Implementing a FIFO queue with two LIFO stacks
Code
// Valid parentheses — O(n), using a stack
function isValid(s) {
const stack = [];
const pairs = { ")": "(", "]": "[", "}": "{" };
for (const ch of s) {
if (ch === "(" || ch === "[" || ch === "{") stack.push(ch);
else if (stack.pop() !== pairs[ch]) return false;
}
return stack.length === 0;
}BuildImplement a min-stack (getMin in O(1)), and solve “daily temperatures” using a monotonic stack.
Self-checkWhy can you implement a FIFO queue using two LIFO stacks — walk through the mechanism.
STAGE 05
Trees & recursion
Week 4–5Recursion finally clicks on trees, because the recursive structure IS the data structure — every subtree is itself a complete tree.
What you'll learn
- Binary tree traversal: in-order, pre-order, post-order
- Recursive thinking — a base case plus a recursive case, nothing more
- Binary search tree properties and why they matter
Code
// In-order traversal — visits values in ascending order for a BST
function inOrder(node, result = []) {
if (!node) return result; // base case
inOrder(node.left, result); // recurse left
result.push(node.val);
inOrder(node.right, result); // recurse right
return result;
}BuildSolve “maximum depth of a binary tree” and “lowest common ancestor” recursively.
Self-checkWhy does in-order traversal of a BST always produce sorted output?
STAGE 06
Graphs & BFS/DFS
Week 5–6Graphs are trees generalized — most “real world” problems (social networks, maps, dependency resolution) are graph problems wearing a disguise.
What you'll learn
- Adjacency list vs. adjacency matrix, and when to use each
- BFS for shortest path on unweighted graphs vs. DFS for exploring/backtracking
- Representing a grid as an implicit graph
Code
// BFS shortest path on an unweighted graph
function bfsShortestPath(graph, start, target) {
const queue = [[start, 0]];
const visited = new Set([start]);
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;
}BuildSolve “number of islands” (flood fill with DFS/BFS) and “course schedule” (cycle detection via topological sort).
Self-checkWhy does BFS guarantee the shortest path on an unweighted graph but DFS doesn't?
STAGE 07
Dynamic programming
Week 6–7DP is just “recursion plus memoization” — it feels scary mainly because most courses teach the formula without first showing why naive recursion is slow and what memoization actually fixes.
What you'll learn
- Overlapping subproblems — the same subproblem gets solved over and over
- Memoization (top-down) vs. tabulation (bottom-up)
- Recognizing when a problem IS a DP problem
Code
// Naive recursive Fibonacci — O(2^n), recomputes everything
function fibNaive(n) {
if (n <= 1) return n;
return fibNaive(n - 1) + fibNaive(n - 2);
}
// Memoized — O(n), each subproblem solved exactly once
function fibMemo(n, memo = {}) {
if (n <= 1) return n;
if (memo[n]) return memo[n];
return memo[n] = fibMemo(n - 1, memo) + fibMemo(n - 2, memo);
}BuildSolve “climbing stairs” and 0/1 knapsack — write both the naive recursive version and the memoized version, and compare.
Self-checkWhat two properties make a problem a good candidate for dynamic programming?
STAGE 08
Pattern recognition & mock practice
Week 7–8Knowing 8 individual patterns doesn't help if you can't tell WHICH pattern a brand-new, unseen problem needs — that recognition is the actual interview skill, not the patterns themselves.
What you'll learn
- A repeatable approach: clarify the problem, brute force it, optimize, code it, test it
- A pattern checklist — is it sorted? do I need order preserved? is it secretly a graph?
- Talking through your thinking out loud, the way a real interview expects
Code
// Pattern checklist as pseudocode
if (isSorted || canSort) considerTwoPointer();
if (needsFastLookup) considerHashMap();
if (isTree || isNestedStructure) considerRecursion();
if (isGraphOrGrid) considerBfsOrDfs();
if (hasOverlappingSubproblems) considerDP();
// The problem rarely announces its pattern — you have to notice it
BuildSolve 3 fresh problems cold, out loud, using the checklist above — time yourself and note which pattern you reached for first.
Self-checkA problem asks for the shortest sequence of single-letter word transformations from a start word to an end word. Which pattern from this path applies, and why?