CMD Guide
HomeDSAAdvanced Patterns

Coding Patterns A Cheat Sheet

A coding pattern is a reusable shape that a family of problems shares: the same access pattern on data (sliding window, two ends closing in, a stack of pending candidates, DFS marking visited nodes) recurs whether the values are integers, strings, or graph nodes. Recognizing the shape lets you retrieve an already-optimal template instead of re-deriving an algorithm from scratch under interview time pressure.

Recognize the pattern

PatternConcrete tellCore structure
Counting"frequency", "duplicates", "majority", "anagram"hash map / array of counts
Monotonic Queue/Stack"max or min in every window of size k", "next greater element"deque kept strictly increasing or decreasing
Simulationexplicit step-by-step rules, no closed-form shortcutdirect state machine
Linear Sort (Counting/Radix/Bucket)values bounded by a small range (e.g. heights 1–100)bucket by value, not by comparison
Meet in the Middlen ≤ ~40, subset-sum / partition flavor, no per-subset state neededsplit in half, enumerate each half, merge with binary search
Mo's Algorithmmany offline range queries whose answer can't be maintained by a Fenwick/segment tree (e.g. count-distinct, mode)sort queries by block, slide two pointers, reuse previous window's state
Serialize/Deserialize"design a codec" for a tree or graphpre-order/BFS encoding with null sentinels
Clone"deep copy" a graph/list with random or cross pointershash map old-node → new-node while traversing
Articulation Point/Bridge"critical connections", removing a node/edge disconnects the graphDFS with discovery time + low-link value

Brute force to optimal, worked on Monotonic Queue

Take the sliding-window-maximum family (e.g. Continuous Subarrays, Sum of Subarray Minimums). The brute force recomputes the extremum of every window from scratch: for each of the n-k+1 windows, scan k elements, giving O(n·k) time and O(1) extra space. The optimal approach keeps a deque of indices whose values are monotonically decreasing (for a max-window); each element enters and leaves the deque at most once, so the whole pass is O(n) time, O(k) space for the deque.

Complexity derived from first principles

Let each array element be pushed onto the deque exactly once (n pushes total). Once pushed, an element leaves the deque in exactly one of two mutually exclusive ways: it is popped from the back when a later, strictly larger element arrives and makes it useless as a future maximum, or it is popped from the front once its index falls outside the current window. It can never suffer both fates — whichever removal happens first takes it out of the structure for good. So total removals across the whole run are ≤n, not 2n (n from the back plus n from the front would double-count elements that are never actually in the deque long enough to be evicted twice). Total deque operations = n pushes + ≤n pops = O(n), not O(n·k). Amortized cost per array element is O(1) even though a single step can pop several elements, because each pop is paid for by that same element's one earlier push. Space is O(k) for the deque (at most one entry per index currently in the window).

Traced example

Array = [5, 3, 4, 1, 2], window k = 3. Deque stores indices; we keep values decreasing. This trace is chosen specifically so the front-pop branch actually fires at i=3, not just the back-pop branch.

ivalpop-back while smallerdeque after pushpop-front if out of windowwindow max (i≥2)
05[0]
13none (3<5, keep)[0,1]
24pop 1 (val 3<4)[0,2]front is 0, window is [0,2] — still valid5
31none (1<4, keep)[0,2,3]window is now [1,3]; front index 0 < 1 → pop it → [2,3]4
42pop 3 (val 1<2)[2,4]window is [2,4]; front index 2 still valid4

At i=3 the back-check finds nothing to pop (1 is smaller than the back element, so it's just appended), yet index 0 has aged out of the window and must be evicted from the front — this is the mechanism the amortized-cost argument depends on, now actually exercised rather than asserted.

Mechanism sketch: the other eight patterns

Counting

Anagram check on "aab" vs "aba": one pass increments a 26-slot counter for the first string, a second pass decrements it for the second; if every slot ends at zero, they're anagrams. O(n) time, O(alphabet) space.

count[26] = 0
for c in s: count[c - 'a']++
for c in t: count[c - 'a']--
return all(x == 0 for x in count)

Simulation

Robot moves on a grid with obstacles: for each instruction, compute the candidate next cell; if it's not an obstacle, commit the move, otherwise stay put and continue. No shortcut exists because state depends on the exact path taken, so cost is O(total steps).

pos = (0,0)
for instr in instructions:
  next = apply(pos, instr)
  if next not in obstacles: pos = next

Linear Sort (Counting Sort)

Sorting heights bounded to 1–100: tally each value into a 101-slot bucket array, then rewrite the array by walking buckets in order. No comparisons are made, so the O(n log n) comparison-sort floor doesn't apply — cost is O(n + k) where k is the value range.

count[101] = 0
for h in heights: count[h]++
out = []
for v in 1..100: append v to out, count[v] times

Meet in the Middle

Subset-sum with target T over n≤40 items: split into halves A and B, enumerate all 2^(n/2) subset sums of A and sort them, then for each of the 2^(n/2) subset sums of B binary-search for T−sum(B) in the sorted A sums. Total O(2^(n/2)·n). This only works because the two halves combine by simple addition — there's no per-element state to carry across the split.

Mo's Algorithm

Given offline range queries (l, r) where the aggregate can't be decomposed into prefix sums (e.g. "how many distinct values in [l,r]"), sort queries by (l / blockSize, r), then move l and r pointers one step at a time between consecutive queries, incrementally updating a running answer as elements enter/leave the window. Total pointer movement across all queries is O((n+q)·√n) because each block's queries share a nearly-fixed l.

Serialize/Deserialize

Encode a binary tree by a pre-order DFS that writes each node's value, using a sentinel like "#" for null children, comma-separated. Decoding replays the same DFS order, consuming tokens left to right and recursively rebuilding left-then-right subtrees. Both directions are O(n).

serialize(node): if node is null: return "#"
  return node.val + "," + serialize(node.left) + "," + serialize(node.right)

Clone

Deep-copying a graph with a random/cross pointer: DFS or BFS from the start node, keeping a map from old node to its clone. Before recursing into a neighbor, check the map first — if the neighbor is already cloned, reuse that clone instead of recursing again. This map lookup is what prevents infinite recursion on cycles.

clone(node, map):
  if node in map: return map[node]
  copy = new Node(node.val); map[node] = copy
  for nbr in node.neighbors: copy.neighbors.append(clone(nbr, map))
  return copy

Articulation Point/Bridge (Tarjan)

DFS from any node, assigning each node a discovery time disc[u] and a low-link low[u] = the smallest discovery time reachable from u's subtree via at most one back edge. u is an articulation point if it's the DFS root with ≥2 tree children, or if some child v has low[v] ≥ disc[u] (v's subtree can't reach above u without going through u). A bridge is the edge (u,v) itself when low[v] > disc[u]. Single DFS pass, O(V+E).

Pitfalls

When to use / when not, with trade-offs

Use pattern-matching when the problem's constraints (n size, value range, query count, whether queries are offline, whether state must be tracked per-subset) point unambiguously at one template — it turns a 45-minute derivation into a 5-minute retrieval. Two trade-offs are easy to state wrong, so state them carefully:

Mo's Algorithm vs Fenwick/segment tree. For a query type a Fenwick or segment tree CAN decompose (range sum, range min, etc.), the tree is asymptotically better at every scale: O(log n) per query beats Mo's O((n+q)√n) total whenever the batch is large, since √n grows faster than log n. Mo's real justification is not raw speed on ordinary range queries — it's that it handles aggregates a tree structure cannot maintain incrementally at all, such as count-distinct or mode-of-range, where there is no cheap merge operation to build a tree on. Reach for Mo's only when that decomposition genuinely doesn't exist, and never on a mutating array (Mo's has no efficient update path).

Meet in the Middle vs DP over bitmask. These are not competing solutions to the same problem, so comparing their complexities head-to-head is the wrong frame. Meet in the Middle (O(2^(n/2)·n)) applies when the answer for the whole set can be recovered by combining two independently-enumerated halves via a simple operation like addition-then-binary-search (subset-sum, partition into equal halves) — it is asymptotically far cheaper than DP over bitmask (O(2^n·n)) at every n, not just large n (n=20: ~2×10^3 vs ~2×10^7). DP over bitmask is chosen instead only when the problem needs per-subset state that can't be split cleanly — e.g. TSP or Hamiltonian path, where the best cost to reach node v depends on the exact set of nodes already visited, and that set can't be decided independently in two halves and merged afterward. This is also why bitmask DP's practical n cap (~20–22) is much lower than meet-in-the-middle's (~40): its cost genuinely is worse, which is exactly why it's reserved for problems meet-in-the-middle structurally cannot express.

Takeaways

Recall: In the trace on [5,3,4,1,2] with k=3, why did index 0 get removed from the deque at i=3 even though no back-pop condition was triggered that step?


Compiled from standard interview-pattern references (sliding window / monotonic deque, meet-in-the-middle, Mo's algorithm, Tarjan's articulation-point/bridge algorithm) and the original course pattern list.

🤖 Don't fully get this? Learn it with Claude

Stuck on Coding Patterns A Cheat Sheet? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Coding Patterns A Cheat Sheet** (DSA) and want to truly understand it. Explain Coding Patterns A Cheat Sheet from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Coding Patterns A Cheat Sheet** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Coding Patterns A Cheat Sheet** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Coding Patterns A Cheat Sheet** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes