Patterns, not problems.

Grinding 400 random LeetCode problems teaches you 400 solutions. This path teaches you the ~8 patterns those problems are actually built from, so a problem you've never seen still feels familiar. Nine stages, real code, mock practice at the end.

Duration
7–8 weeks
Format
Pattern-based
Prerequisite
JavaScript track
You'll build
A problem-solving playbook

Every stage is one pattern, fully earned

Why the pattern exists, what it actually solves, a real code example, two problems to build it on, and one question that checks you understand the mechanism — not just the syntax.

A live playground, right here

No setup, no account. This is Stage 1's two-pointer Two Sum, straight from the path below — edit it, break it, fix it.

playground.js
Console
Run the code to see output here.

This is what's waiting at the end

Certificate of Completion
Your Name
has completed Patterns, Not Problems — 9 stages, 9 passed quizzes
9 / 9 stages8 core patternsVerified ID
🔒

Complete all 9 stages and their quizzes to unlock this.

Nine stages, in this order

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 1

Most 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,000
BuildTake 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–2

Two-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 2

Hash 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 3

Linked 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–4

Stacks 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–5

Recursion 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–6

Graphs 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–7

DP 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–8

Knowing 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?

The capstone: your own playbook

Write up all 8 patterns in your own words — when to reach for each, your own solved example, and the mistake you made learning it. This is the document you actually reopen the night before an interview, not a certificate.

One page per pattern, in your own words
A real solved example you wrote yourself
3 fresh problems solved cold, timed
Notes on which pattern you reached for first, and why
Kept somewhere you'll actually reopen it
No copy-pasted solutions — explain or it doesn't count

Common pitfalls

  • LeetCode grinding without patterns. 400 solved problems with no pattern recognition is 400 memorized answers, not a skill.
  • Reading the solution too fast. Give a problem 25–30 real minutes before you look — that struggle is where the pattern actually sticks.
  • Skipping brute force. If you can't write the slow, obvious solution first, you don't understand the problem well enough to optimize it.
  • Silent practice. Solving in silence doesn't train the skill interviews actually test — narrate your thinking out loud, every time.

Resources worth your time

  • NeetCodepatterns
  • LeetCodepractice
  • VisuAlgovisualize
  • Introduction to Algorithms (CLRS)reference
  • Big-O Cheat Sheetquick ref