Appearance
4.0 — How to Recognise a Problem in Thirty Seconds
Strong problem-solvers are not faster thinkers. They are people who have seen the shape before, so the first thirty seconds of reading produces a template rather than a blank page.
Part 4 builds that, in one repeating rhythm: a chapter teaches a structure or a technique, and the chapter straight after it works every NeetCode 150 problem that uses it, one problem per page. So hashing is Chapter 4.3 and its nine problems are Chapter 4.4; heaps are 4.16 and their seven problems are 4.17; dynamic programming is 4.22 and its twenty-three problems are 4.23 and 4.24. Read straight through in order and nothing ever appears before the thing it is built from.
Every problem page is laid out the same way, because the layout is the method: the problem restated plainly · what you already know that solves it · the brute force and the exact thing that is wasteful about it · the idea, in one sentence · the code with every line explained and the intuition behind it · the complexity with the non-obvious part derived · the edge cases · and the generalisation — which other problems this same move solves.
1. The recognition table
This is the page to come back to. Left column is what you read in the problem; right column is what to reach for.
| What the problem says | Pattern | Page |
|---|---|---|
| "have I seen this", "duplicate", "count of each" | hash map / set | 4.4.0 |
| "group by some property" | hash on a fingerprint | 4.4.0 |
| "sum of a range", asked many times | prefix sum | 4.4.0 |
| sorted array, find a pair / triple | two pointers from both ends | 4.5.0 |
| "in place", "remove", "partition" | write pointer | 4.5.0 |
| "palindrome" | two pointers inward | 4.5.0 |
| contiguous subarray or substring, "longest/shortest/at most K" | sliding window | 4.6.0 |
| "valid parentheses", "nesting", "undo" | stack | 4.8.0 |
| "next greater / smaller element", "span" | monotonic stack | 4.8.0 |
| sorted input, or "minimise the maximum" | binary search, incl. on the answer | 4.11.0 |
| "O(\log n) required" | binary search | 4.11.0 |
| linked list, "reverse / middle / cycle" | slow-fast pointers, dummy head | 4.9.0 |
| tree, "path / depth / subtree" | DFS, usually post-order | 4.14.0 |
| tree, "level", "shortest" | BFS with a queue | 4.14.0 |
| "prefix", "starts with", "dictionary" | trie | 4.15.0 |
| "top k", "k-th largest", "median of a stream" | heap of size k | 4.17.0 |
| "merge k sorted things" | heap | 4.17.0 |
| "all combinations / permutations / subsets", "generate every" | backtracking | 4.18.0 |
| grid of cells, "islands / regions / flood" | BFS or DFS over the grid | 4.20.0 |
| "prerequisites", "ordering", "dependency" | topological sort | 4.20.0 |
| "shortest path" with weights | Dijkstra | 4.21.0 |
| "connect everything cheaply" | MST — Kruskal with union-find | 4.21.0 |
| "are these connected", edges arriving | union-find | 4.21.0 |
| "how many ways", "maximum/minimum over choices" | DP | 4.23.0 · 4.24.0 |
| two strings compared | 2-D DP grid | 4.24.0 |
| "maximum profit / fewest coins", locally obvious best move | greedy — then try to break it | 4.26.0 |
| pairs of (start, end) | intervals, sort first | 4.27.0 |
| "without division", "in O(1) space", digits | math tricks | 4.28.0 |
| "appears once", "no extra memory", "count bits" | XOR and bit tricks | 4.30.0 |
2. The five questions to ask before writing anything
Ask these in order, every time. They take under a minute and they prevent the two failures that cost people offers: solving the wrong problem, and writing an O(n^2) solution when the constraints demanded O(n \log n).
1. What am I given, and what is the size? The constraints are a hint, not decoration. This table is worth having memorised — it maps the input size to the complexity the setter has in mind, assuming roughly 10^8 simple operations per second:
| n up to | Intended complexity |
|---|---|
| 10–12 | O(n!) — permutations |
| 20–25 | O(2^n) — subsets, bitmask DP |
| 100–500 | O(n^3) — interval DP, Floyd-Warshall |
| 1,000–5,000 | O(n^2) — 2-D DP over both indices |
| 10^5–10^6 | O(n \log n) — sort, heap, binary search |
| 10^7+ | O(n) or O(\log n) — one pass, hashing, two pointers |
"n up to 20" is the setter telling you an exponential answer is expected. "n up to 10^6" is them telling you that sorting is the most you can afford.
2. What is the brute force, and what is its complexity? Always say it out loud. It proves you understand the problem, it gives you a correct fallback, and — most importantly — it tells you what to attack. "Brute force is O(n^2) because for every element I scan every other element to check if I have seen it" contains its own fix: the scan is the waste, so hash it.
3. What is being recomputed? Almost every optimisation is one of four moves.
- Recomputing a lookup → hash map.
- Recomputing a sum or count over a window → sliding window or prefix sum.
- Recomputing a subproblem → memoisation (Chapter 4.22).
- Recomputing a comparison against everything → sort first, then two pointers or binary search.
4. Is there structure I am ignoring? Sorted input means binary search or two pointers. Non-negative values mean a sliding window can shrink safely. A bounded alphabet means a fixed-size array instead of a map. "Exactly one solution exists" means you can return early. The problem statement rarely wastes words.
5. What are the edge cases? Empty input, one element, all identical, negative numbers, integer overflow, and duplicates. Name them before coding, handle them in the code, and mention them when you finish.
3. The moves that appear everywhere
Nine ideas account for most of the 150 problems. Every one has already been built in Chapters 4.1–4.29, and this list is what they look like when a problem is trying to hide them.
Trade space for time. The single most common optimisation. A nested loop asking "does this exist elsewhere" becomes one loop plus a hash set. O(n^2) to O(n) at the cost of O(n) memory.
Sort to create structure. Sorting costs O(n \log n) and buys you two pointers, binary search, greedy ordering and adjacency of equal elements. If the brute force is O(n^2) and sorting does not destroy what you need, sorting is usually the first thing to try.
Two pointers instead of two loops. When you can prove that moving one pointer never needs to be undone, you replace O(n^2) with O(n). The proof is always the same shape: this element cannot be part of any better answer, so discarding it is safe.
A window with a shrink rule. For contiguous ranges. Expand the right edge always, shrink the left edge while a condition is violated. Both pointers only move forward, so it is O(n) even though it looks nested — the amortized argument from Chapter 4.1.
Compute once, answer many times. Prefix sums, precomputed hashes, a preprocessed table. Pay O(n) up front, answer each query in O(1).
Remember what you have already solved. Memoisation. The tell is a recursion tree with repeated nodes.
Keep only what can still win. The monotonic stack and monotonic deque discard elements that are provably useless. Each element enters and leaves once, so the whole thing is linear.
Search the answer, not the data. When the answer is a number and "is X achievable" is easier than "what is the best X", binary search the answer space. Chapter 4.10 called this searching a monotonic predicate.
Reverse the direction. Working backwards from the target, iterating from the right, or reversing the graph's edges. Several problems that are painful forwards become trivial backwards.
4. How to use these pages
Each problem group opens with a .0 page — the pattern itself. That page carries four things: the recognition cue (the exact phrases in a problem statement that mean this pattern), the annotated template with the line that changes between problems marked, the traps people fall into on this pattern specifically, and what the interviewer will push on. Then one page per problem, in the order NeetCode lists them, which is roughly easiest first.
The Blind 75 subset is flagged with a ★. If you are short on time, do the starred ones across every group rather than all of one group — coverage of shapes beats depth in one shape.
The code is Python first, TypeScript second. Python because that is what most people type into the judge and what the interviewer will least often argue with; TypeScript in a second tab because the rest of Volume I is written in it and because a few of these problems teach something about JavaScript specifically. Where the two languages genuinely disagree — integer division, how a tuple can be a dictionary key, what [[]] * n does to you — the page says so at the point where it would bite.
A note on how to practise. Reading a solution and understanding it produces almost no retention. What produces retention is: read the problem, say the pattern out loud before scrolling, then check. If you were right, move on — you do not need to write the code. If you were wrong, that is the problem worth writing out in full. The recognition is the skill; the code is the easy part once you have it.
Next: 4.1 gives you the vocabulary every one of those answers is written in — how to count the cost of a piece of code, and how to tell in advance which complexity the problem setter is asking for.