The Ultimate Coding Interview Cheat Sheet
This is a coding interview cheat sheet built for the last hour before a round, not for a twelve-week study plan. Everything here is the stuff worth having in working memory: the patterns that cover most questions, the complexities you will be asked to state out loud, and the templates that are easy to get subtly wrong under pressure.
Skim it top to bottom in about ten minutes. Come back to the pattern table when you are stuck.
The pattern recognition table
Most interview problems are one of about a dozen patterns wearing a costume. The skill being tested is recognizing which one, fast. Read this column-first: find the signal in the question, then the pattern.
| Signal in the question | Pattern | Typical cost |
|---|---|---|
| Sorted array, find a pair or triple | Two pointers | O(n) |
| "Contiguous subarray" or "substring" | Sliding window | O(n) |
| "Top k", "k largest", "k closest" | Heap of size k | O(n log k) |
| "Sorted" plus "find" or "minimum value that…" | Binary search | O(log n) |
| Tree, "level by level" | BFS with a queue | O(n) |
| Tree, "path" or "depth" | DFS with recursion | O(n) |
| Grid, "islands", "regions", "flood fill" | DFS or BFS on the grid | O(rows × cols) |
| Grid or graph, "shortest path", unweighted | BFS | O(V + E) |
| Weighted graph, "shortest path" | Dijkstra | O(E log V) |
| "All combinations", "all permutations" | Backtracking | Exponential |
| "Count the ways", "min or max cost to…" | Dynamic programming | Usually O(n) or O(n²) |
| "Next greater", "valid parentheses" | Stack | O(n) |
| "Detect a cycle" in a linked list | Fast and slow pointers | O(n) |
| Overlapping intervals | Sort by start, then sweep | O(n log n) |
| "Order that satisfies dependencies" | Topological sort | O(V + E) |
| Prefix matching, autocomplete | Trie | O(length) |
| "Groups that merge together" | Union-Find | ~O(1) amortized |
If nothing matches, ask two questions: what is the brute force, and what work is it repeating? Almost every optimization in this list is the answer to the second one. Hash map removes repeated lookups. Sliding window removes repeated summation. DP removes repeated subproblems.
Complexity, from memory
You will be asked for these directly, and hesitating costs credibility even when the code is right.
Data structure operations
| Structure | Access | Search | Insert | Delete |
|---|---|---|---|---|
| Array | O(1) | O(n) | O(n) | O(n) |
| Dynamic array | O(1) | O(n) | O(1) amortized | O(n) |
| Linked list | O(n) | O(n) | O(1) at a known node | O(1) at a known node |
| Hash map | — | O(1) average | O(1) average | O(1) average |
| Balanced BST | O(log n) | O(log n) | O(log n) | O(log n) |
| Heap | O(1) for min or max | O(n) | O(log n) | O(log n) |
| Trie | O(L) | O(L) | O(L) | O(L) |
Hash map operations are O(n) in the worst case. Say "average" out loud; interviewers notice.
Sorting
| Algorithm | Average | Worst | Space | Stable |
|---|---|---|---|---|
| Quicksort | O(n log n) | O(n²) | O(log n) | No |
| Mergesort | O(n log n) | O(n log n) | O(n) | Yes |
| Heapsort | O(n log n) | O(n log n) | O(1) | No |
| Timsort (Python, Java) | O(n log n) | O(n log n) | O(n) | Yes |
| Counting sort | O(n + k) | O(n + k) | O(k) | Yes |
What the input size tells you
The constraints are a hint about the intended complexity. Read them before you start.
| n up to | Target complexity |
|---|---|
| 10–12 | O(n!) — permutations are fine |
| 20–25 | O(2ⁿ) — subsets, bitmask DP |
| 500 | O(n³) |
| 5,000 | O(n²) |
| 10⁶ | O(n log n) |
| 10⁸ | O(n) or O(log n) |
If n ≤ 20, stop looking for the clever polynomial solution. Exponential is the answer.
Templates worth memorizing
These are the ones where an off-by-one under pressure costs five minutes of debugging in front of an audience.
Binary search, no off-by-one
def binary_search(arr, target):
lo, hi = 0, len(arr) - 1
while lo <= hi:
mid = lo + (hi - lo) // 2 # avoids overflow in fixed-width languages
if arr[mid] == target:
return mid
if arr[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
The variant that matters more in interviews is searching for a boundary rather than an exact value — "smallest value for which this predicate is true":
def lower_bound(arr, predicate):
lo, hi = 0, len(arr) # hi is exclusive here
while lo < hi:
mid = lo + (hi - lo) // 2
if predicate(arr[mid]):
hi = mid # keep mid as a candidate
else:
lo = mid + 1
return lo
Note the two differences: hi starts at len(arr), and the loop is < not <=. Mixing the two templates is the single most common binary search bug.
Sliding window, variable size
def longest_valid_window(s):
counts, left, best = {}, 0, 0
for right, ch in enumerate(s):
counts[ch] = counts.get(ch, 0) + 1
while not is_valid(counts): # shrink until valid again
counts[s[left]] -= 1
if counts[s[left]] == 0:
del counts[s[left]]
left += 1
best = max(best, right - left + 1)
return best
Every variable-size window problem is this shape. Only is_valid changes.
BFS on a grid
from collections import deque
def bfs(grid, start):
rows, cols = len(grid), len(grid[0])
queue = deque([(start, 0)])
seen = {start}
while queue:
(r, c), dist = queue.popleft()
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols \
and (nr, nc) not in seen and grid[nr][nc] != "#":
seen.add((nr, nc))
queue.append(((nr, nc), dist + 1))
return -1
Mark cells as seen when you enqueue, not when you dequeue. Marking on dequeue lets the same cell enter the queue many times and quietly turns a linear traversal into something much worse.
Backtracking
def backtrack(path, choices, results):
if is_complete(path):
results.append(path[:]) # copy — the list keeps mutating
return
for i, choice in enumerate(choices):
if not is_valid(path, choice):
continue
path.append(choice)
backtrack(path, remaining(choices, i), results)
path.pop() # undo
The path[:] copy and the matching pop() are where this goes wrong. Append and pop must be exactly paired around the recursive call.
Dynamic programming
Start top-down, because the recurrence is easier to see and memoization is one decorator:
from functools import cache
@cache
def solve(i, state):
if i == n:
return base_case
return best(
solve(i + 1, state), # skip
value(i) + solve(i + 1, next(state)), # take
)
Convert to bottom-up only if the interviewer asks about space, or recursion depth is a real risk. Saying "I'd memoize this first and convert to an iterative table if we need the space" is a good answer on its own.
Union-Find
parent = list(range(n))
rank = [0] * n
def find(x):
while parent[x] != x:
parent[x] = parent[parent[x]] # path compression
x = parent[x]
return x
def union(a, b):
ra, rb = find(a), find(b)
if ra == rb:
return False # already connected
if rank[ra] < rank[rb]:
ra, rb = rb, ra
parent[rb] = ra
rank[ra] += rank[ra] == rank[rb]
return True
Edge cases to say out loud
Naming these before you code earns credit even when the input never hits them. It also catches roughly half of all interview bugs before they happen.
- Empty input, and input of length one
- All elements identical
- Already sorted, and sorted in reverse
- Negative numbers, and zero
- Integer overflow, in fixed-width languages
- Duplicates, when the problem implies uniqueness
- A single node, or a completely disconnected graph
- A cycle, where you assumed a tree
- Unicode and multi-byte characters, in string problems
The talk track
Interviewers score communication separately from correctness, and it is the easier of the two to improve. A reliable structure:
- Restate the problem in one sentence and confirm it.
- Ask about constraints. Input size, value ranges, duplicates, whether the input can be mutated.
- Give one concrete example, including a tricky one.
- State the brute force and its complexity. Do not skip this — it establishes a baseline and buys thinking time.
- Name the optimization and why it works. "We're recomputing the same sum, so a running window removes that."
- State the target complexity before writing code.
- Write it, narrating decisions rather than syntax.
- Trace your example by hand. Out loud. This catches most bugs.
- Name what you would test, and what you would change at scale.
Being stuck is not disqualifying. Being stuck and silent is. "I'm considering two approaches and I'm not sure which handles duplicates better" is a strong sentence.
The hour before
- Re-read the pattern table above, not new problems.
- Re-solve one problem you have already done, to warm up the talk track.
- Check your setup: editor, font size, screen share, microphone.
- Close notifications. Nothing kills a shared screen faster than a message preview.
- Have water within reach. Talking for 45 minutes is dry work.
Do not learn a new algorithm in the last hour. It will not stick and it will crowd out something you already know.
Even well-prepared candidates blank under time pressure — recall in a live round is a different skill from recall at a desk. Cloak is a free, native macOS interview copilot that reads the problem on your screen and returns an approach in seconds, invisibly to Zoom, Meet, and Teams. Download it free, or read the live coding interview playbook next.