Ch 30 / 30 Patterns, Interviews and the Capstone 0/0 exercises Exercises ↓

AmouAI Hub/Courses/Data Structures & Algorithms/Chapter 30

Part 7 · Advanced and Applied · Chapter 30

Patterns, Interviews and the Capstone

Fifteen recognisable patterns, a procedure for attacking a problem you have never seen, and a capstone that makes you use eight structures in one system.

Reading
Skiena Ch. 1
Focus
Recognition
Cost
Needs
Chapters 1–29

By the end of this chapter you can

  1. Recognise fifteen recurring problem patterns from their wording
  2. Follow a repeatable seven-step procedure on a problem you have never seen
  3. Infer the intended complexity from the input size before you have an idea
  4. Choose a data structure by the operations a problem performs, not by its subject matter
  5. Build the capstone: a route planner combining a graph, a heap, a hash map, an LRU cache and a trie
  6. Know what to study next, and in what order

1Fifteen patterns

Most problems you will meet are one of these wearing a costume. Recognition is the skill; the code is the easy part.

PatternThe tellChapter
Two pointersA sorted array, a pair or triple summing to something4
Sliding window“Contiguous subarray/substring” with a constraint4
Prefix sumsMany range-sum queries, no updates4
Fast and slow pointersA cycle, a midpoint, the k-th from the end5
Monotonic stack“Next greater”, “largest rectangle”, spans6
Hash map countingAnagrams, duplicates, “seen before?”8
Binary search on the answer“Minimum capacity such that…”, a monotone predicate13
Tree traversalDepth, paths, validation, serialisation14–15
Top-K with a heap“k largest”, “k closest”, a running median17
BFS on a grid or graphFewest steps, shortest in an unweighted graph20
DFS / connected components“How many islands”, reachability, cycles20
Topological sortDependencies, ordering, prerequisites21
Dijkstra / A*Weighted shortest path, cheapest route22
Dynamic programmingCount the ways, optimise under a budget25–26
Backtracking“Find all”, permutations, board configurations27
Read the operations, not the story

A problem about scheduling deliveries and a problem about compiling modules are the same problem if both are “order these under dependency constraints”. The subject matter is decoration.

So translate the statement into operations before you think about a solution: what do I insert, what do I look up, what do I ask for repeatedly, and what has to stay in order? Those four answers pick the structure, and the structure usually picks the algorithm.

A doctor's differential

A doctor does not derive a diagnosis from first principles. They take a history, match the pattern against a few dozen conditions they know well, and order a test to distinguish between the two or three that fit.

That is the right model here. You are not inventing algorithms; you are recognising which of fifteen shapes this is, then checking the constraints to distinguish between the candidates that fit.

2A procedure for an unseen problem

Seven steps, in order. The order matters more than any individual step.

  1. Clarify. What are the input ranges? Can values be negative, empty, duplicated? What should happen on invalid input? Two minutes here saves twenty later, and in an interview it is half of what is being assessed.
  2. Work a small example by hand. Not to test a solution — to find one. The steps you take manually are usually the algorithm, and the moment you feel yourself doing something repetitive is the moment you have spotted the subproblem.
  3. State the brute force. Out loud, with its complexity. It proves you understand the problem and it gives you something correct to compare against. Never skip it because it is “obvious”.
  4. Name the bottleneck. Where exactly is the brute force wasting effort? Repeated work (→ memoise), repeated scans (→ hash map or precomputation), repeated sorting (→ heap), rediscovering an order (→ sort once).
  5. Match a pattern. The table in §1. The bottleneck usually names it for you.
  6. Code it. Only now. Say what you are about to write before you write it.
  7. Test deliberately. Empty input, one element, all-equal elements, the maximum size, negatives, and the specific edge your algorithm is fragile at — you know where it is.
The size of n tells you the answer

Assume roughly 10⁸ simple operations per second. Then the input bound in the problem statement is a very strong hint about the intended complexity:

n up toIntended complexityWhich usually means
10–12O(n!)Permutations, brute force
20–25O(2ⁿ)Subsets, bitmask DP
100–500O(n³)Interval DP, Floyd-Warshall
1,000–5,000O(n²)Two-sequence DP, all pairs
10⁵O(n log n)Sorting, heap, binary search
10⁶–10⁷O(n)One pass, two pointers, counting
10⁹ and beyondO(log n) or O(1)Maths, binary search on the answer

If n ≤ 20, stop looking for a clever polynomial algorithm — the problem is telling you it wants an exponential one. If n = 10⁶ and you are contemplating a nested loop, you already know it is wrong.

What an interview is actually measuring

Not whether you have memorised the answer. The things that visibly separate candidates:

  • Asking about constraints before coding.
  • Saying the brute force out loud, with its complexity, before optimising.
  • Thinking audibly — silence reads as being stuck even when you are not.
  • Testing your own code before being asked to.
  • Handling a hint gracefully. A hint is information, not a judgement.

A candidate who reaches a clean O(n log n) while explaining their reasoning does better than one who silently produces the optimal answer from memory.

3Picking the structure

The whole course, compressed into one table: what you need to do, and what does it fastest.

What you need to doStructureCost
Index by positionArrayO(1)
Insert or delete at the endsDeque / dynamic arrayO(1) amortised
Insert or delete in the middle, given the nodeDoubly linked listO(1)
Look up by keyHash mapO(1) average
Keep keys sorted, and find neighboursBalanced BST / sorted listO(log n)
Repeatedly take the smallest or largestHeapO(log n)
Last-in-first-outStackO(1)
First-in-first-outQueueO(1)
Prefix lookup over stringsTrieO(length)
“Are these two connected?” as edges arriveUnion-find~O(1)
Range query plus point updateFenwick / segment treeO(log n)
Bounded cache with recency evictionHash map + doubly linked listO(1)
Approximate membership, memory-boundBloom filterO(k)
The four questions

When you cannot decide, answer these in order:

  1. How do I find things? By position → array. By key → hash map. By order → tree or heap. By prefix → trie.
  2. What changes, and how often? Nothing → precompute. Single elements → a tree structure. Whole ranges → lazy propagation.
  3. Does order matter? Insertion order → list or deque. Sorted order → tree. Only the extreme → heap.
  4. Is memory the binding constraint? Then consider Fenwick over segment tree, bitsets over booleans, and probabilistic structures over exact ones.
Kitchen drawers

Nobody organises a kitchen by what things are made of. You organise it by when you reach for them: everyday cutlery in the top drawer, the roasting tin at the back of a cupboard, knives where your hand goes without looking.

Data structures are the same. Not “this is a list of users”, but “I look users up by id a thousand times a second and add one a minute” — which is a hash map, whatever a user is.

4The capstone

One system that needs a graph, a heap, a hash map, an LRU cache, a trie, a set, a queue and a sort. Nothing here is new; assembling it is the exercise.

Build a route planner. A small city map, a query interface, and enough load that naive choices show up. The specification:

RequirementWhat it forces you to useChapter
Store the road networkAdjacency list19
Cheapest route between two placesDijkstra with a heap17, 22
Fewest-turns route, ignoring distanceBFS20
Repeat queries answered instantlyLRU cache keyed on (from, to)29
Autocomplete on place namesTrie18
“Which places are reachable at all?”Union-find or DFS20, 23
The cheapest set of roads to grit in winterMinimum spanning tree23
Rank the ten busiest junctionsHeap or partial sort11, 17
Avoid recomputing on an unchanged mapInvalidate the cache on edit
capstone.pya skeleton, not a solution
class RoutePlanner:
    """The shape of the capstone. Each method is a chapter of this course."""

    def __init__(self):
        self.graph = {}          # adjacency list          -> Ch. 19
        self.trie = Trie()       # place-name autocomplete  -> Ch. 18
        self.cache = LRUCache(128)   # recent route queries -> Ch. 29
        self.version = 0         # bumped on every edit, so the cache can be keyed on it

    def add_road(self, a, b, minutes):
        """Adding a road must invalidate cached routes."""

    def cheapest(self, a, b):
        """Dijkstra, with the LRU cache in front of it."""

    def fewest_turns(self, a, b):
        """BFS — a different question, a different algorithm, the same graph."""

    def suggest(self, prefix, k):
        """The k best-known places starting with prefix."""

    def gritting_plan(self):
        """A minimum spanning tree over the whole network."""

    def busiest(self, k):
        """The k junctions appearing in the most cached routes."""
The three lessons the capstone actually teaches
  1. One graph, many questions. Cheapest, fewest-turns, reachable, cheapest-to-connect are four different algorithms over the same adjacency list. The structure is shared; the traversal is not.
  2. Caching is a correctness problem, not a performance one. The moment add_road exists, every cached answer might be stale. Deciding when to invalidate is harder than any algorithm in the chapter, and it is the part real systems get wrong.
  3. Structures compose. The LRU is a hash map plus a list; the trie holds a heap of popular completions; Dijkstra is a graph plus a heap. Almost nothing at this level is one structure on its own.

The exercises below build the pieces — autocomplete, top-K, a running median, a rate limiter — and then assemble a working planner. Take them in order; the last one uses the earlier ones.

5Where to go next

What you have, what you do not, and a sensible order to fill the gaps.

You now have the structures and algorithms that account for the overwhelming majority of practical work: arrays and their costs, linked structures, hashing, trees and balance, heaps, tries, graphs and their four main traversals, sorting and searching, and the three design paradigms with the judgement to choose between them.

What is missing, roughly in the order it becomes worth learning:

  • Amortised analysis properly — the potential method, which explains why splay trees and Fibonacci heaps work at all.
  • Randomised algorithms — skip lists, treaps, randomised quickselect, Karger's min cut. Often simpler than the deterministic version and just as good in practice.
  • Network flow — max flow, min cut, bipartite matching. A surprisingly large family of problems reduces to it, and recognising that is a genuine superpower.
  • Computational geometry — convex hulls, sweep lines, closest pair.
  • String structures — suffix arrays, suffix automata, Aho–Corasick.
  • NP-completeness — recognising that a problem is probably intractable, so you stop looking for an exact fast algorithm and start looking for a good enough one.
  • Approximation and heuristics — what to do once you know it is NP-hard.
  • Concurrent and external-memory structures — lock-free queues, B-trees, LSM trees. This is where database engineering lives.
The habit worth keeping

The single most valuable thing to take from thirty chapters is not any structure. It is the reflex of asking, about every piece of code you write:

“What does this cost, and what happens when the input is a thousand times bigger?”

That question is what separates code that works from code that keeps working. Everything else in this course is the vocabulary for answering it.

Learning to read music

You can now read the notation. You have played through the standard repertoire once, you know what a fugue is when you hear one, and you can tell a difficult passage from an easy one at sight.

What you do not yet have is fluency, and there is only one way to get it: play. Solve problems you have not seen. Read other people's solutions after you have written your own. Implement a structure from its description rather than from example code.

The gap between recognising an algorithm and reaching for it unprompted is closed by repetition, and by nothing else.

What to carry forward

  • Fifteen patterns cover most problems. Recognition is the skill; the code is the easy part.
  • Translate a problem into operations — insert, look up, ask repeatedly, keep in order — before choosing anything.
  • The procedure: clarify, work an example by hand, state the brute force, name the bottleneck, match a pattern, code, test.
  • The input bound tells you the intended complexity. n ≤ 20 means exponential is expected; n = 10⁶ means one pass.
  • The bottleneck names the fix: repeated work → memoise, repeated scans → hash map, repeated sorting → heap.
  • Interviews measure clarifying, narrating, and testing at least as much as the final answer.
  • Choose a structure by how you find things, what changes, whether order matters, and whether memory binds.
  • One graph answers many different questions — cheapest, fewest-turns, reachable, cheapest-to-connect are four algorithms over one adjacency list.
  • Caching is a correctness problem. Deciding when to invalidate is harder than the algorithm it is caching.
  • Real systems compose structures: an LRU is a map plus a list, Dijkstra is a graph plus a heap.
  • Keep one habit: what does this cost, and what happens at a thousand times the size?

>_Playground

A pattern-matcher you can feed a problem statement to, a complexity budget calculator, and a working route planner assembled from the whole course.

scratch.pypython not loaded
Real Python, running in your browser. Nothing is installed or uploaded.
Output appears here. The first run takes a few seconds while Python loads.

Exercises

Checked automatically the moment you submit. Work top to bottom — each one assumes the last. Your answers are saved in this browser.

All Warm-up Core Challenge Reset chapter

That is the course

Thirty chapters, from what an array is to A* and segment trees. Where next depends on what you want to build.

All courses →