dsa-prep/patterns-cheatsheet.md

17 — Patterns Cheatsheet

1. The Trigger Lookup Table

~5 min read·updated 5/29/2026

17 — Patterns Cheatsheet

The single page you'll come back to most often. Read a problem → identify the pattern → reach for the template.

Table of Contents

  1. The Trigger Lookup Table
  2. Decision tree: which pattern?
  3. Templates at a glance
  4. Complexity targets per input size
  5. Pattern → Pattern dependencies

1. The Trigger Lookup Table

Read the problem statement and look for these key phrases.

Phrase / shapePatternFile
"Two Sum"-style lookup, "find pair with property"Hashmap (or two pointers if sorted)01, 02
"anagram", "duplicate", "frequency"Counter / hash01
"sum of subarray", "range sum query"Prefix sums01
"find pair / triplet sum" in sorted arrayTwo pointers (opposing)02
"in-place modify, keep order"Two pointers (same direction)02
"longest/shortest substring with property"Sliding window03
"window of size k" / "k consecutive"Fixed sliding window03
"max/min of every k-window"Monotonic deque03
"next greater / previous smaller / span / largest rectangle"Monotonic stack04
"matching parens / brackets / RPN"Stack (LIFO)04
"find target in sorted" / "rotation point"Binary search05
"minimize max" / "maximize min" with a feasibility checkBinary search on answer05
"cycle in linked list / find middle"Fast & slow pointers06
"Nth from end of linked list"Two-pointer gap06
"build / mutate / reverse linked list"Dummy head + four-line reversal06
"LRU / O(1) get-set-evict cache"Doubly linked list + hashmap06
"level order / right side view / minimum depth"BFS on tree07
"compute X for each subtree"Postorder DFS07
"diameter / max path sum"Local + global tracker07
"validate BST / kth smallest in BST"Inorder + bounds07
"autocomplete / prefix dictionary / starts-with"Trie08
"word search II (multiple words on grid)"Trie + grid DFS08
"K largest / smallest / closest / frequent"Heap of size k09
"running median / two halves"Two heaps09
"merge K sorted lists / arrays"Heap of heads09
"schedule tasks with cooldown"Heap + queue09
"all subsets / permutations / combinations"Backtracking10
"n-queens / sudoku / word search (grid)"Backtracking with constraints10
"shortest path in unweighted graph / grid"BFS11
"shortest in grid where blocked / walls"BFS11
"all reachable / connected components / islands"DFS / BFS11
"course prerequisites / topological order"Topological sort (Kahn or DFS)11
"shortest path with non-negative weights"Dijkstra12
"shortest path with negative weights"Bellman-Ford12
"shortest with at most k edges/stops"Bellman-Ford with k iterations12
"merge sets / detect cycle in undirected"Union-Find12
"minimum spanning tree"Kruskal (Union-Find) or Prim (heap)12
"count ways / number of paths"DP13, 14
"min cost / max profit with overlapping subproblems"DP13, 14
"knapsack / subset sum / target sum"0/1 knapsack DP14
"edit distance / LCS / interleave"2D string DP14
"burst balloons / matrix chain"Interval DP14
"merge / overlap / schedule intervals"Sort + greedy sweep15
"all sources spread simultaneously"Multi-source BFS11
"find single element among duplicates"XOR16
"n ≤ 20 with subsets / states"Bitmask DP16

2. Decision tree: which pattern?

START
  │
  ├── Linked list given? ───────────── 06-linked-list.md
  │     ├── needs in-place reverse? → 4-line reversal
  │     ├── cycle? middle? Nth?       → fast/slow pointers
  │     └── O(1) cache?               → DLL + hashmap (LRU)
  │
  ├── Tree given? ────────────────── 07-trees.md
  │     ├── BST?                       → inorder gives sorted
  │     ├── level-by-level?            → BFS
  │     ├── compute for each subtree?  → postorder DFS
  │     └── diameter / max path?       → local + global
  │
  ├── Graph or grid? ───────────────── 11/12
  │     ├── unweighted shortest?       → BFS
  │     ├── weighted, ≥0?              → Dijkstra
  │     ├── weights with negatives?    → Bellman-Ford
  │     ├── all reachable?             → DFS / BFS
  │     ├── ordering of dependencies?  → topo sort
  │     └── merge groups / cycle in undirected? → Union-Find
  │
  ├── Sorted array / monotonic answer? ─ 05-binary-search.md
  │     ├── find element / boundary?   → binary search
  │     └── "min X to do Y" feasibility? → BS on answer
  │
  ├── "Best subarray / substring with property"? ─ 02 / 03
  │     ├── pair / triplet sum (sorted)? → two pointers
  │     └── window with constraint?    → sliding window
  │
  ├── "Top K / kth / median"? ──────── 09-heaps.md
  │     └── heap (size k) or two heaps
  │
  ├── "All subsets / permutations / combinations"? ── 10
  │     └── backtracking
  │
  ├── "Min/max cost or count of ways"
  │   with overlapping sub-decisions? ─ 13/14 (DP)
  │     ├── 1D state (linear)?         → 13-dp-1d.md
  │     ├── 2D state (two indices, knapsack)? → 14-dp-2d.md
  │     └── intervals?                 → interval DP
  │
  ├── "Schedule / overlap / merge intervals"? ── 15
  │     └── sort by start or end + greedy sweep
  │
  ├── "Find unique / count bits / power of 2"? ── 16
  │     └── bit tricks
  │
  └── (none of the above)
        ├── n ≤ 12         → maybe brute force / backtracking
        ├── n ≤ 1e3        → O(n²) DP / two-loops fine
        └── n ≤ 1e5        → must be O(n log n) or better

3. Templates at a glance

Two pointers (opposing)

l, r = 0, len(arr) - 1 while l < r: if arr[l] + arr[r] == target: ... elif arr[l] + arr[r] < target: l += 1 else: r -= 1

Sliding window (variable longest)

l = 0 state = init() best = 0 for r, x in enumerate(arr): add(x) while not valid(state): remove(arr[l]); l += 1 best = max(best, r - l + 1)

Binary search (predicate / lower_bound)

lo, hi = 0, len(arr) while lo < hi: mid = lo + (hi - lo) // 2 if P(mid): hi = mid else: lo = mid + 1 return lo

BFS

from collections import deque q = deque([start]) visited = {start} while q: n = q.popleft() for nbr in neighbors(n): if nbr not in visited: visited.add(nbr) q.append(nbr)

DFS (iterative or recursive)

def dfs(node): if base_case: return visited.add(node) for nbr in neighbors(node): if nbr not in visited: dfs(nbr)

Backtracking

def bt(state): if is_solution(state): record(state); return for choice in choices(state): if not valid(choice, state): continue apply(choice, state) bt(state) undo(choice, state)

Heap (top-K largest)

import heapq h = [] for x in nums: if len(h) < k: heapq.heappush(h, x) elif x > h[0]: heapq.heapreplace(h, x) return list(h)

Topological sort (Kahn)

indeg = [...] q = deque(v for v in nodes if indeg[v] == 0) order = [] while q: v = q.popleft(); order.append(v) for nbr in graph[v]: indeg[nbr] -= 1 if indeg[nbr] == 0: q.append(nbr) # valid if len(order) == n

Dijkstra

import heapq dist = {start: 0} pq = [(0, start)] while pq: d, u = heapq.heappop(pq) if d > dist.get(u, float('inf')): continue for v, w in graph[u]: nd = d + w if nd < dist.get(v, float('inf')): dist[v] = nd heapq.heappush(pq, (nd, v))

Union-Find

parent = list(range(n)) rank = [0] * n def find(x): if parent[x] != x: parent[x] = find(parent[x]) return parent[x] def union(x, y): rx, ry = find(x), find(y) if rx == ry: return False if rank[rx] < rank[ry]: rx, ry = ry, rx parent[ry] = rx if rank[rx] == rank[ry]: rank[rx] += 1 return True

DP (1D / 2D)

# Bottom-up template dp = [base for _ in range(n + 1)] for i in range(1, n + 1): dp[i] = recurrence(dp, i) return dp[n] # Top-down template @lru_cache(maxsize=None) def f(state): if base_case(state): return base_value return combine(f(smaller_state) for ...)

Monotonic stack (next greater)

stack = [] # decreasing values, top-down res = [0] * n for i, x in enumerate(arr): while stack and arr[stack[-1]] < x: j = stack.pop() res[j] = x or i - j stack.append(i)

4. Complexity targets per input size

n upper boundacceptable complexitytypical algorithm
10O(n!)brute permutations
20O(2ⁿ)bitmask, backtracking
100O(n⁴)obscure DPs
500O(n³)Floyd-Warshall, MCM
5,000O(n²)DP, two-loops
100,000O(n log n)sort + scan, heap, balanced tree
10⁶+O(n)linear scan, hash, single-pass
10⁸+O(log n) per querybinary search, segment tree

If you find your solution exceeds the budget, that's the signal to change patterns.


5. Pattern → Pattern dependencies

foundations
   │
   ├── arrays-hashing ──────┐
   ├── two-pointers         │
   ├── sliding-window       │
   ├── stack                │
   ├── binary-search ◄──────┤  (BS-on-answer reuses arrays)
   ├── linked-list          │
   ├── trees ◄──────────────┤  (use stack / queue)
   ├── tries ◄──────────────┤  (use trees)
   ├── heaps ◄──────────────┤  (use trees)
   ├── backtracking         │
   ├── graphs ◄─────────────┤  (BFS uses queue, DFS uses stack)
   ├── advanced-graphs ◄────┤  (Dijkstra = heap; Union-Find = forest)
   ├── dp-1d ◄──────────────┤  (foundations + recursion)
   ├── dp-2d ◄──────────────┤  (extends dp-1d)
   ├── greedy-intervals     │
   └── bit-manipulation

Master the linear chain top-to-bottom and you can read any LeetCode problem.


6. The 60-second decision

When you read a problem, give yourself 60 seconds:

00–20s   Read carefully. Identify input shape and size.
20–40s   Match against this cheatsheet's triggers.
40–60s   Articulate (out loud, even alone) the chosen pattern + complexity.

If at 60s you have no candidate, state that to the interviewer and ask clarifying questions while you think.

If you have one, sketch the template and start filling in.


TL;DR ✅

This file is the TL;DR.

But if you must memorize three things:

  1. Two pointers + sliding window solves most array/string subproblems.
  2. BFS / DFS / topological sort plus Dijkstra / Union-Find covers all graphs.
  3. DP = optimal substructure + overlapping subproblems, written top-down or bottom-up.

Everything else is variation.

Next: 18-interview-strategy.md.

// 2 views

main
UTF-8·typescript