{} Coding Interviews · ch.03 · big-o in anger
🧩 Part I · The Game · chapter 3 / 24

Big-O, used
in anger

Nobody at the whiteboard wants a proof — they want you to read n from the constraints and refuse to write a doomed loop. This chapter turns complexity from an exam topic into a weapon: a budget, a table, and a thirty-second elimination ritual.

In school, Big-O is something you prove. In an interview, it's something you use — before writing a single line — to kill the approaches that cannot possibly finish in time. The constraints block at the bottom of every problem is not legal boilerplate; it's the interviewer telling you, in a coded whisper, which of the twenty patterns are still in the running.

This is the last chapter of Part I's meta-game. Chapter 2 gave you the six-step loop; this chapter arms step three ("brute force out loud, name its cost") and step four ("find the pattern") with actual numbers. After this, every pattern chapter can say things like "n is 10⁵, so the nested loop is illegal" and you'll nod instead of squinting.

1A veto, not a proof — complexity as elimination

Here's the reframe that makes this chapter short: in the room, you will almost never be asked to derive a bound with limits and constants. You'll be asked, implicitly and constantly, to not choose a doomed approach. Complexity analysis in anger is a veto power: it strikes candidates off the list so the survivors get your attention.

That flips the usual workflow. Most people read the story, invent an approach, and only then wonder if it's fast enough. The pattern-recognition move is the reverse: read the constraints first, compute what's affordable, and let that decide which patterns are even allowed to bid. Half your search space disappears before you've thought about the actual problem — which is the entire point of Chapter 1's thesis: spend your in-room brainpower on the disguise, not the mechanism.

Three facts make the veto cheap to run: computers do a roughly knowable number of operations per second, growth rates separate brutally fast, and problem-setters choose their constraint numbers on purpose. The next three sections give you each fact as a tool.

💡
Interviewers score the veto itself. Saying "n is 10⁵, so anything quadratic is dead — I need O(n log n) or better, which means sorting, hashing, or a window" is pure signal: it's the problem-solving dial and the communication dial moving at once, and you haven't written any code yet.

2One second, a hundred million steps — the ops budget

The whole system runs on one rule of thumb: about 10⁸ simple operations per second. Array reads, additions, comparisons — the cheap stuff a tight compiled loop does. Online judges typically give you one or two seconds, so 10⁸ is your budget; an approach that needs 10¹⁰ operations isn't "a bit slow", it's a hundred seconds of silence followed by a timeout.

The rule is deliberately fuzzy — and that's fine, because growth rates are so violently different that a factor of ten in the budget almost never changes the verdict. Watch what happens at n = 10⁵:

  • O(n) — 10⁵ ops. Finishes before your finger leaves the Enter key.
  • O(n log n) — "sort once, then walk" — about 1.7×10⁶ ops. Trivial.
  • O(n²) — 10¹⁰ ops. A hundred times over budget. Dead, and no clever constant saves it.
  • O(2ⁿ) — a number with thirty thousand digits. Not slow; fictional.

Notice there's no borderline case in that list. That's typical: problem-setters pick n so the intended complexity fits comfortably and the next class up busts by a factor of 100+. The budget question almost always has a crisp answer.

⚠️
The Python asterisk: interpreted Python does more like 10⁷ heavy operations per second, so borderline plans (say, O(n²) at n = 10⁴, exactly 10⁸ ops) that squeak by in C can time out in Python. In the interview room nobody's running a judge — but say "this is borderline, and in Python I'd want the next class down" and watch the verification dial twitch upward.

3Race the curves — six growth rates, one budget line

You've seen the growth-rate chart in every textbook. Here it is with the one thing textbooks leave out: the budget line. Both axes are logarithmic — each gridline is a factor of ten — and the dashed ochre line is your 10⁸ budget. Drag n, or press race and watch the classes die in order: O(2ⁿ) before n hits 30, O(n²) at n = 10⁴, O(n log n) in the millions. The green survivors at the bottom are why "sort it" and "hash it" are the reflexes of Chapters 4–9.

Interactive · growth-curve racer Log-log chart · dashed line = 10⁸ ops · dots go red past the budget
n = 10³
10³
within budget
— / 6
slowest survivor
first casualty

Two readings worth taking away. First, the exponential curve is barely on the chart — it exits the top before n reaches 60, which is why exponential patterns (backtracking, Chapter 18) are only ever sanctioned for tiny n. Second, look how close O(n) and O(n log n) run: the log factor costs about 17× at n = 10⁵. Rule of thumb: log factors are almost free; polynomial degrees are life and death.

4n is a confession — the constraints table

Because problem-setters choose n to make the intended solution comfortable, the constraint line runs backwards: from n, you can read off the complexity they expect, and from the complexity, the patterns that produce it. This is the table to burn into memory — it's arguably the highest-value half page in the book:

  • n ≤ 20O(2ⁿ) or even O(n·2ⁿ) is fine → backtracking (ch. 18), bitmask enumeration (ch. 22). 2²⁰ ≈ 10⁶.
  • n ≤ ~400O(n³) survives → triple loops, all-pairs work. 400³ ≈ 6×10⁷.
  • n ≤ ~5,000O(n²) → honest nested loops, the simpler DP tables (ch. 20–21).
  • n ≈ 10⁵ – 10⁶O(n log n) or O(n)sorting, heaps (ch. 14), binary search (ch. 9), hashing (ch. 4), sliding window (ch. 6), monotonic stacks (ch. 8). This is where most Mediums live.
  • n ≈ 10⁹ or beyond — or n is a lone number, not an array length → O(log n) or O(1) → binary search on the answer (ch. 9, think Koko Eating Bananas Medium), math and bit tricks (ch. 22, think Pow(x,n) Medium).

The table cuts both ways. n ≤ 100 with a problem that smells like it wants something clever? The constraint says the interviewer will accept O(n²) — or even O(n³) — so state the simple version and ship it. Gold-plating a solution the budget didn't ask for costs time and buys nothing.

🎯
The tell: n ≤ 20 in the constraints is the interviewer whispering "backtracking is fine." Tiny n is never an accident — it's permission for exponential work, usually because the answer itself (all subsets, all arrangements) is exponentially large.
🎯
The tell: "1 ≤ nums.length ≤ 10⁵" outlaws your inner loop. Before reading the story, your shortlist is already: sort it, hash it, window it, stack it, or binary-search it. That's five chapters of this book pre-selected by one line of constraints.

5Read constraints like a poker player — the sniff drill

Time to make the table a reflex. The widget deals you real constraint lines; your job is to name the worst complexity that still fits the 10⁸ budget — worst, because that's the ceiling the problem-setter designed for, and knowing the ceiling keeps the widest set of patterns available. The widget actually computes the operation counts, so the verdicts are arithmetic, not vibes.

Interactive · constraint sniffer Pick the worst (slowest) class that still fits 10⁸ ops
constraint
1 / 6
nailed exactly
0
streak
0

Notice the drill's third verdict, the yellow triangle: picking a class that fits but underestimates the ceiling isn't wrong, but it narrows your options for no reason — like folding a winning hand. If the budget allows O(n²) and you can only think of the O(n²) answer, that's not a failure. That's Tuesday.

6Paying average, not worst — amortized cost in one picture

One word trips people up in complexity conversations: amortized. You've relied on it forever — every list.append, every ArrayBuffer += — because dynamic arrays double their capacity when full. Most pushes cost one write; occasionally a push lands on a full array and pays to copy everything into a bigger one. Worst case for a single push: O(n). And yet append is honestly called O(1). How?

Because the expensive pushes are exponentially rare. Doubling from capacity 4 means copies happen at sizes 4, 8, 16, 32… — the total copy work for n pushes is n/2 + n/4 + … < n, a geometric series. Spread ("amortized") over all n pushes, total cost stays under 3 writes per push, forever. The widget below runs the real machine: watch the spikes get taller and rarer, while the running average flatlines.

Interactive · amortized push-pop Spikes = resize copies · dashed line = running average cost
last push: —
pushes
0
capacity
4
total writes
0
amortized avg
⚠️
The follow-up trap: "amortized O(1)" is a statement about totals, not about any individual operation. If an interviewer pivots to "what if this is a latency-sensitive system?", the honest answer is that one unlucky push still pays O(n) — that pivot is a systems question wearing an algorithms costume, and noticing it is the point. Hash maps carry the same asterisk: O(1) average, with rare resize spikes.

7Space counts too — and sorted input is a gift

Time gets the drama, but space is scored with the same budget logic — 10⁵ integers is nothing, 10⁵ × 10⁵ booleans is 10 GB of "wait, no". Three space facts pay rent in interviews:

  • The classic trade runs memory → time. Chapter 1's Two Sum Easy bought O(n) time with O(n) space. When an interviewer adds "…in O(1) space", they're confiscating that trade and pointing you at a pointer trick — two pointers (ch. 5), fast-slow on lists (ch. 10), or in-place rewiring.
  • Recursion isn't free. Every recursive call sits on the stack, so a DFS over a path-shaped tree is O(n) space whether you allocated anything or not. Saying "O(1) extra space, plus the recursion stack" marks you as someone who's been bitten — in a good way.
  • Output can dominate. "Return all subsets" of 20 elements is 2²⁰ answers; no algorithm beats the size of its own output. That's another reason tiny n and "return all…" travel together.

Finally, the flip side of the elimination game: constraints take options away, but one word in a problem statement gives options. If the input is already sorted — or sortable without breaking anything — a whole discount catalogue opens: binary search (ch. 9) finds anything in O(log n), two pointers (ch. 5) replace nested scans, duplicates become neighbors, and merging is linear. Half the patterns in Part II are cheap precisely because sortedness prepaid the hard part.

🎯
The tell: the word "sorted" in a problem statement is a gift certificate — the setter paid O(n log n) so you don't have to. If you finish a solution without having spent it, stop: there's almost certainly a log-factor discount you walked past.
🎯
The tell: a gigantic n (10⁹, 10¹⁸) with a "minimize the largest…" or "can you do it with k?" flavor means the answer space is monotonic — binary-search the answer, not the array. Chapter 9 makes this a superpower; it echoes again in Chapter 17's Swim in Rising Water Hard.

8The thirty-second elimination — running it in the room

Here's the whole chapter as a habit. When a problem appears, your eyes go to the constraints before the story — the way a chess player counts material before admiring the position. Thirty seconds of arithmetic buys you a pattern shortlist, a spoken cost estimate for your brute force, and the quiet confidence of someone who knows the nested loop is illegal before writing it. Chapter 23's decision tree starts exactly here: constraint size is one of its first questions.

And remember the deal from Chapter 2: the brute force still gets said out loud, always — "brute force is all pairs, n², which at 10⁵ is 10¹⁰ — over budget, so I need n log n or better" is one sentence that scores on three dials. The budget doesn't replace the brute force; it prices it.

The pattern, as a whiteboard skeleton:

  1. 1Read the constraints block first. Before the story. Write nmax in a corner of the whiteboard.
  2. 2State the budget: ~10⁸ simple ops ≈ one second (÷10 for Python, and say so).
  3. 3Find the ceiling: the worst complexity where cost(nmax) still fits — n ≤ 20 → 2ⁿ · n ≤ 5,000 → n² · n ≈ 10⁵ → n log n · n ≈ 10⁹ → log n.
  4. 4Translate ceiling → shortlist: 2ⁿ → backtracking · n² → nested loops / DP tables · n log n → sort, heap, binary search · n → hash, window, stack · log n → binary-search the answer.
  5. 5Price the brute force out loud and compare it to the budget — if it fits, say so and just build it.
  6. 6Spend the gifts: is the input sorted? Is there an "amortized O(1)" structure (dynamic array, hash map) doing the heavy lifting?
  7. 7Check space too: extra structures, recursion depth, output size — same budget arithmetic.
  8. 8After coding, re-price: confirm the thing you built matches the complexity you promised.

9Contains Duplicate, three price tags — same answer, different bills

One problem, priced three ways. Contains Duplicate Easy: does any value appear at least twice? The brute force compares every pair — at n = 10⁴ that's 49,995,000 comparisons in the worst case, and at n = 10⁵ it's five billion: dead on arrival. Sorting first makes duplicates neighbors (section 7's gift, bought at retail), so one adjacent-scan suffices: about 1.2×10⁵ comparisons at n = 10⁴, four hundred times cheaper. And the hash set — Chapter 4's superpower, previewed — asks "have I seen this?" in O(1): 10,000 lookups, done.

The counts in the comments are worst-case measurements at n = 10⁴ with no duplicate present (the most expensive case — early exit never fires). Read the three functions as three rungs of the elimination ladder: what the constraints allow decides which rung you're expected to stand on.

💡
The three versions are also the three most common unstick moves from Chapter 2: when a brute force busts the budget, "try sorting it" and "try a hash map" are the first two levers to pull — they fix the cost of exactly the operation ("have I seen this?") that made the brute force quadratic.