Stack · LIFO
Nesting, undo, expression parsing, next greater/smaller, iterative DFS.
Invariant: the top is the most recent unresolved item.Interview problem-solving playbook
Translate the prompt into states, transitions, and required fast operations. The algorithm usually becomes visible after those three decisions.
# Convert story into mechanics
state = what_changes()
transition = one_legal_move()
bottleneck = repeated_operation()
structure = make_fast(bottleneck)
algorithm = explore(state, transition)
say_invariant_out_loud()
test_small_and_hostile_cases()
01 · The interview loop
Do not jump from the problem statement directly to code. Produce one concrete artifact at each step.
Define input, output, duplicates, mutation, empty cases, and whether an answer always exists.
Artifact: one precise sentence.Trace a tiny normal case and one hostile boundary case by hand.
Artifact: expected state changes.State the obvious complete solution before optimizing it.
Artifact: baseline complexity.Identify repeated search, recomputation, ordering, or traversal.
Artifact: the expensive operation.Select the structure that makes the expensive operation cheap.
Artifact: operation → structure mapping.Name the invariant and why every transition preserves it.
Artifact: one correctness claim.Implement helpers, test boundaries, and recompute time and space complexity.
Artifact: accepted-quality implementation.02 · Stack or queue?
The removal order—not the syntax of the input—selects the structure.
Nesting, undo, expression parsing, next greater/smaller, iterative DFS.
Invariant: the top is the most recent unresolved item.Arrival order, scheduling, level traversal, unweighted shortest steps, BFS.
Invariant: all earlier-discovered work leaves first.Repeated minimum/maximum, top-k, Dijkstra, event simulation.
Invariant: the root is the best live candidate.Sliding-window extrema, 0–1 BFS, front/back scheduling.
Invariant: dominated or expired candidates are removed.03 · Three binary-search families
All three repeatedly discard half of a monotonic search space, but their return value and boundary invariant are different.
Use when values are sorted and any exact matching index is acceptable.
[lo, hi]while lo <= hilo = mid + 1 or hi = mid − 1.
Open template →
Use for insertion points, duplicates, first/last occurrence, lower bound, or upper bound.
[lo, hi)while lo < hia[i] >= target gives lower bound; a[i] > target gives upper bound. Last occurrence is upper bound − 1.
Open template →
Use when answers are numeric and a check changes monotonically from impossible to possible.
feasible(mid)After some boundary, every later candidate must stay true—or every later candidate must stay false.
State exactly which part of the range may still contain the answer.
Inclusive ranges discard mid; first-true ranges keep a valid mid with hi = mid.
Dry-run empty, one-element, two-element, all-equal, absent, and boundary answers.
04 · BFS, DFS, or graph?
Grids, word transformations, puzzle configurations, dependency systems, and trees can all be modeled as graphs.
Explore by distance layers. Mark when enqueued to prevent duplicate work.
BFS template →Either works. Prefer DFS for recursive structure; BFS when layers are useful.
Graph DFS template →Use colors for cycle detection or indegrees when prerequisites become ready.
Topological template →Choose, recurse, undo. Keep only state belonging to the current branch.
Backtracking template →Nonnegative: Dijkstra. Negative: Bellman–Ford. Small all-pairs: Floyd–Warshall.
Weighted path flow →Define exactly what dfs(node) returns, then combine child results.
Tree DFS template →05 · Dynamic programming chooser
DP is organized exhaustive search: define a state, guess a transition, reuse repeated subproblems, and evaluate dependencies safely.
Climbing stairs, house robber, decoding. State usually ends at index i.
dp[i]LCS, edit distance, grid paths. State tracks two changing axes.
dp[i][j]Capacity, sum, count, or budget. Iterate backward for 0/1 and forward for reusable items.
dp[capacity]Burst balloons, matrix chain, palindrome partitioning. Grow by interval length.
dp[left][right]Robber on tree, diameter, subtree choices. DFS returns the states needed by the parent.
dfs(node, state)Assignments, traveling salesperson, pairing. A mask records which items are consumed.
dp[mask][last]Count numbers satisfying digit rules without enumerating the entire numeric range.
dp[pos][tight][state]Longest paths in DAGs and dependency scoring. Evaluate in topological order.
dp[node]LIS, word break, segmentation, and optimal partitions. Try the last previous choice.
dp[end]Define the state before the recurrence
If two recursive calls have the same state coordinates, their future answer must be identical. Anything that changes future choices belongs in the state; history that no longer matters does not.
dp[i] = ways to reach step icame from i−1 or i−2dp[i] = dp[i−1] + dp[i−2]dp[0] = 1, dp[1] = 1left → rightThe same state appears in multiple branches and repeats the same work.
Each unique state is computed once; later calls reuse its stored value.
06 · Backtracking decision tree
A backtracking state contains the current decision index and the partial solution. Each edge makes one choice; returning across the edge undoes it.
Add the candidate or modify the board/state.
Solve the remaining decisions from the new state.
Restore exactly what the choice changed before trying its sibling.
Stop a branch as soon as it cannot lead to a valid or better solution.
Enumerate valid solutions, arrangements, or paths. Prune impossible branches.
Cache one answer per state instead of exploring the repeated subtree again.
07 · Combine data structures
Write the required operations and target complexity first. Combine structures when each contributes a different fast operation.
Lookup, remove, reorder, minimum, random access, history, or range query.
Choose a structure for each operation it performs naturally.
Store an index, iterator, node pointer, timestamp, or shared key between structures.
Every insert, update, and delete must keep all representations consistent.
| Required operations | Combine | Why it works | Typical problems |
|---|---|---|---|
| O(1) lookup + move + oldest eviction | Hash map + doubly linked list | Map jumps to the node; list changes recency and removes both ends. | LRU cache |
| O(1) lookup + frequency + LRU tie-break | Hash maps + frequency buckets + linked lists | One map finds nodes; another groups nodes by frequency; each bucket preserves recency. | LFU cache |
| O(1) insert + delete + uniform random | Dynamic array + value-to-index map | Array gives random indexing; map locates deletions; swap-with-last avoids shifting. | Randomized set |
| Online insert + immediate median | Max-heap + min-heap | Heap roots expose the two middle candidates while balancing partitions the stream. | Streaming median |
| Versioned values + latest value before time | Hash map + sorted history arrays | Map selects the key history; append preserves order; binary search selects a version. | Time map |
| Stack operations + current minimum | Value stack + prefix-minimum stack | Both stacks move together; the auxiliary top stores the minimum at that depth. | Min stack |
| O(1) count updates + min/max key | Key map + doubly linked count buckets | Keys jump to buckets; adjacent buckets handle ±1 count; ends expose min and max. | All O(1) structure |
| Replace index + smallest index for value | Index map + value-to-ordered-set map | One map removes stale membership; each ordered set exposes its smallest index. | Number containers |
| Array updates + historical snapshots | Per-index version lists + binary search | Store only changed values with snapshot IDs, then locate the requested version. | Snapshot array |
| Follow graph + newest items from many streams | Hash sets + per-user lists + max-heap | Sets track follows; lists preserve posts; heap performs a k-way merge of recent heads. | Design Twitter |
| Count quickly + repeatedly extract top-k | Hash map + heap | Map aggregates frequencies; heap maintains only the best candidates. | Top-k frequent items |
| Minimum priority + update/delete named item | Heap + hash map / index map | Heap selects the minimum; map locates mutable entries. | Schedulers, indexed priority queue |
| Prefix search + payload lookup | Trie + hash map | Trie narrows by characters; map stores metadata or counts. | Autocomplete, word dictionary |
| Connectivity + component metadata | DSU + arrays / hash maps | DSU finds roots; attached storage tracks size, sums, or labels per root. | Accounts merge, dynamic components |
Invariant: every node belongs to exactly one frequency bucket; minFreq names the first eviction bucket.
Invariant: the map index always points to the value's current array slot.
Invariant: lower ≤ upper and their sizes differ by at most one.
Invariant: each key's versions remain ordered by timestamp.
Invariant: buckets are count-ordered and every key lives in its matching bucket.
Official problem →Invariant: both directions agree after every replacement.
Official problem →Invariant: each minimum entry summarizes the matching value-stack prefix.
Invariant: each index stores only ordered changes, not a full copy per snapshot.
Official problem →Case study · LRU cache
Neither structure is enough alone. A map cannot identify the oldest key, while a list cannot find an arbitrary key in O(1).
Open the LRU C++ / Python templatekey → node addressO(1) average lookupMap miss → −1. Map hit → move node to front and return its value.
Update value, then move the existing node to the front.
Create node, add at front, and store its address in the map.
Remove the node before the tail and erase the same key from the map.
08 · Research basis
The flows synthesize established teaching material; wording and examples here are original and adapted for quick interview use.