Skip to the chapter
←DSAGGroundworkΣDSA
DSA/Advanced/A14
48 / 48 written chapters
/

Levels and plan
  1. B1Complexity analysis
  2. B2JavaScript toolkit
  3. B3Arrays & strings
  4. B4Prefix sums
  5. B5Hashing
  6. B6Two pointers
  7. B7Sliding window
  8. B8Binary search
  9. B9Basic recursion
  10. B10Sorting algorithms
  11. B11Stacks & queues
  12. B12Linked lists
  1. I1Trees
  2. I2BST operations
  3. I3Tree problems in depth
  4. I4Heaps & priority queues
  5. I5Graphs: representation
  6. I6BFS on grids
  7. I7Graph problems
  8. I8Topological patterns
  9. I9Backtracking
  10. I10DP: 1D
  11. I11DP: 2D
  12. I12DP state machines
  13. I13Greedy algorithms
  14. I14Intervals
  15. I15Bit manipulation
  16. I16Matrix problems
  17. I17Tries
  18. I18Monotonic stack & queue
  19. I19Math for interviews
  20. I20Design & simulation
  21. I21Divide and conquer
  22. I22Randomization
  1. A1Advanced DP
  2. A2Union-Find
  3. A3Advanced graph algorithms
  4. A4Minimum Spanning Tree
  5. A5Advanced graphs
  6. A6Segment & Fenwick trees
  7. A7Sparse tables
  8. A8String algorithms
  9. A9Design problems
  10. A10Advanced backtracking
  11. A11Games & probability DP
  12. A12Ordered structures
  13. A13Sweep line & geometry
  14. A14Interview strategy

ΣA14 · Part 3 · Advanced

Interview strategy

The algorithm is half the score — the other half is everything you say before, during and after writing it.

45 min read2 diagrams16 sections7 exercises
48 of 48

In this chapter

  • What is actually being scored
  • The 45 minutes as checkpoints, not hopes
  • The first five minutes: questions that change the answer
  • A framework that works on a problem you have never seen
  • Constraints → intended complexity: the highest-leverage table here

and 11 more

about 45 min
16On this page
  1. What is actually being scored
  2. The 45 minutes as checkpoints, not hopes
  3. The first five minutes: questions that change the answer
  4. A framework that works on a problem you have never seen
  5. Constraints → intended complexity: the highest-leverage table here
  6. Keywords to a first guess, and the check before you commit
  7. How to talk while you code
  8. Being stuck, handled well
  9. Pivoting off a wrong approach without losing the interview
  10. Proving it correct in one sentence
  11. Test before you say "done"
  12. Follow-ups: it is a stream, it is sorted, it does not fit on one machine
  13. Discussing tradeoffs at a senior level
  14. When the problem is NP-hard: recognise it, then say the right thing
  15. A full mock: one Hard problem, start to finish
  16. Signals that separate an advanced performance from an intermediate one

What is actually being scored

At the advanced level the interviewer is not checking whether you can produce a correct program. They are estimating one thing: what is it like to hand this person an ambiguous problem and come back in three days? Every behaviour in this chapter is a proxy for that. Clarifying questions predict whether you will build the wrong thing. Stating a brute force before optimizing predicts whether you ship something or stall. Testing your own code predicts whether QA finds your bugs or you do. Naming a tradeoff unprompted predicts whether you will make a defensible technical decision alone.

Two candidates can both produce a working O(n log n) solution and receive "strong hire" and "no hire." The difference is almost never the code. It is that one of them narrated a decision process the interviewer could follow and trust, and the other silently emitted a memorized answer that could not be probed, extended, or debugged out loud.

clarify approach code, narrating test trade- offs 0 5 12 33 40 45 min no code is written before minute 12 — that is deliberate, not slow the common failure: coding at minute 3 → wrong complexity discovered at minute 30, no time to recover, and → the interviewer never saw you reason, only saw you type
The first twelve minutes buy the last thirty-three. A wrong approach caught at minute 8 costs nothing; the same mistake found at minute 30 ends the interview.

The 45 minutes as checkpoints, not hopes

The timeline above says where each phase should sit. A timeline you only glance at does nothing for you; what helps is knowing, at each checkpoint, what must already be true and what you do if it is not. These are guides to calibrate against, not rules: a Hard problem pushes the approach phase longer and a simple one shortens it.

By minuteWhat should be trueIf you are behind
5The problem is restated, the example is answered by hand, and two or three questions that changed your plan have been asked.Stop asking. State your assumptions aloud ("I'll assume non-empty input and say so in the code") and move on.
12A brute force is on the board with its complexity, the bottleneck is named, the constraints have told you the target, and the interviewer has agreed to the plan.No better idea by minute 10 is normal. Say so, and offer to code the brute force while you keep thinking: it is a baseline, partial credit, and often the skeleton of the optimization.
25The main loop or recursion exists. Helpers have names even if their bodies are stubs.Write the core loop first and stub the rest with a one-line contract ("isValid(state): true when no rule is broken"). Working structure beats polished fragments.
33The code is complete enough to run in your head, and you start tracing the example with real values.Trace what exists, say what remains, and finish it as you go. An unfinished function you traced is worth more than a finished one you did not.
40The example and one edge case are traced, any bug is fixed, and you are naming trade-offs.Give each trade-off two sentences. Offer the next improvement instead of starting to build it.
45You have asked one question of your own about the problem or the team.If time is gone, ask it anyway: it is the part you control.

The checkpoint that does the most work is minute 12, because everything after it is expensive to undo. The one at minute 25 catches the commonest failure in the middle of the interview: a candidate who has been coding carefully for thirteen minutes and has a lovely helper function and no program. Look at the clock when you finish the approach phase, not only at the end.

The first five minutes: questions that change the answer

Silence here is the single strongest negative signal available, and it is the cheapest to fix. An engineer who starts coding from an under-specified problem statement is telling the interviewer exactly how they behave with an under-specified ticket. But not all questions are equal — "can I use a hashmap?" wastes the goodwill you are trying to build. Ask only questions whose answer would change your solution, and say why you are asking.

AskWhy it changes the solution
"How large can n get?"The highest-value question in the interview. It fixes your target complexity before you have written anything — see the table below.
"Is the input sorted, or can I sort it?"Sorted unlocks two pointers and binary search for free. If not sorted, an O(n log n) sort may be free anyway when the target is already O(n log n) — but it is not free if the target is O(n).
"Can there be duplicates?"Changes whether you dedupe, whether a Set is safe, whether two-pointer needs a skip loop, and whether the expected output is unique.
"What's the range of the values? Negative? Zero? Floats?"Negatives break sliding-window-on-sums and greedy arguments. Small bounded values unlock counting sort or a bitmask. Floats kill exact equality.
"What should I return for empty input / no valid answer?"An explicit contract, decided up front, instead of an ad-hoc guess at minute 40.
"Can I mutate the input?"In-place sorting or marking may be the whole space optimization; if the input is shared state, it isn't allowed.
"Is this called once, or repeatedly on the same data?"Repeated queries change the answer entirely — preprocess into a prefix array, segment tree, or index map and amortize.
"Does the whole input fit in memory, or is it a stream?"Streaming rules out sorting and random access; pushes toward heaps, reservoir sampling, count-min sketch.
Say it like this → "Before I start — a few things that would change my approach. How big is n? Can values be negative? And are duplicates possible in the input? …n is up to 10⁵ and values can be negative — good, that rules out the sliding-window approach I was about to reach for, since a negative number means the window sum isn't monotonic."

That last clause is the part that scores. You are not collecting facts, you are demonstrating that each fact eliminates a branch of your decision tree. Two or three questions asked this way beat ten asked mechanically.

⚠ Don't ask questions you can answer from the examples If the provided example contains a negative number, asking "can values be negative?" reads as not having read the problem. Skim the examples first, extract what they already settle, and ask only about what they leave open — then say so: "the example has duplicates so I'll assume they're allowed; what I can't tell from it is whether the array is guaranteed non-empty."

A framework that works on a problem you have never seen

You will not recognize the problem. That is the point of an advanced interview. What you need is not recall but a procedure that visibly makes progress even from zero. Run these seven steps out loud, in order.

#StepWhat you actually say
1Restate in your own words"So: given X, return Y, where the constraint is Z. Is that right?"
2Work the given example by handSay the answer for the example before writing anything. Catches misreads instantly.
3State a brute force, with its complexity"The obvious thing is to check every pair — O(n²) time, O(1) space. That's my baseline; let me see if I can beat it."
4Name the bottleneck"The expensive part is that for each i, I re-scan everything before i. That inner scan is the thing to remove."
5Read the constraints for the target"n goes to 10⁵, so O(n²) is 10¹⁰ — far too slow. They're steering me to O(n log n) or O(n)."
6Ask what structure removes the bottleneck"What would make that inner scan O(1) or O(log n)? A hashmap of seen values, a heap, a monotonic stack, or precomputed prefix sums."
7Confirm, then code"So: one pass, hashmap from value to index, O(n) time and O(n) space. Shall I code that?"

Step 3 is non-negotiable and candidates skip it constantly, believing a brute force looks weak. The opposite is true: it guarantees you have a solution on the board within five minutes, it proves you understand the problem, and it gives you a concrete complexity to improve on. An interviewer will almost always let you skip implementing it. What they will not forgive is twenty minutes of silence hunting for the clever answer.

Dry run: the seven steps applied to one concrete problem

"Given a list of daily prices, find the maximum profit from buying once and selling once later. n ≤ 10⁵."

StepApplied hereConclusion
1. Restate"Find the largest prices[j] - prices[i] with j > i."matches the problem
2. Hand-run the example[7, 1, 5, 3, 6, 4]: buy at 1, sell at 6expected answer: 5
3. Brute forcecheck every pair (i, j) with j > iO(n²), stated as the baseline
4. Name the bottleneckfor each i, re-scanning every later j for its pricethe re-scan is the wasted work
5. Read the constraintsn ≤ 10⁵ → O(n²) is 10¹⁰, too slowneed O(n log n) or O(n)
6. Structure that removes ittrack the minimum price seen so far in one passno data structure needed — one running value
7. Confirm, then code"one pass, O(n) time, O(1) space"ready to code

The problem changes every time; the seven-step shape doesn't. That's the actual claim this framework makes — not that you'll recognize this specific problem, but that running the same seven questions on an unfamiliar one gets you from "no idea" to "one pass, O(n)" without needing to have seen it before.

brute force O(n²), stated aloud what work repeats? "I re-scan the prefix" which structure answers it in O(1)? hashmap · prefix sums · heap · monotonic stack · sorted order + two pointers · binary search on the answer · union-find · trie · memoized state optimization is a search over this middle box, not over memorized solutions
Almost every optimization in interview DSA is the same move: identify work being redone, then buy it back with a data structure.

Constraints → intended complexity: the highest-leverage table here

Interviewers and problem setters choose n deliberately. The bound is a hint about the intended solution, and reading it correctly can collapse a twenty-minute search into thirty seconds. Calibrate against the rough industry rule that ~10⁸ simple operations is about one second.

Constraint on nIntended complexityPattern family it points at
n ≤ 10-12O(n!) · O(n! · n)Full permutation search, brute-force TSP, "try every ordering"
n ≤ 20-25O(2ⁿ) · O(2ⁿ · n)Bitmask — subset enumeration, bitmask DP, meet-in-the-middle (2^(n/2)) if n ≈ 40
n ≤ 100O(n³) · O(n⁴)Floyd-Warshall, interval/matrix-chain DP, triple nested loops are fine
n ≤ 1,000-5,000O(n²)2D DP over pairs — edit distance, LCS, palindromic substrings; all-pairs comparison
n ≤ 10⁵O(n log n)Sort-then-scan, heap, binary search on the answer, balanced BST / ordered set, divide and conquer, segment tree
n ≤ 10⁶-10⁷O(n) · O(n log log n)Single pass, two pointers, sliding window, hashmap, counting sort, prefix sums, sieve, Kadane
n ≤ 10⁹-10¹⁸O(log n) · O(√n) · O(1)Math/closed form, binary search over the answer space, matrix exponentiation, digit DP — you cannot even read the input

Read the second half of the constraints too. "Sum of all string lengths ≤ 10⁵" over many strings means linear in the total, which fits a Trie or Aho-Corasick built over all the strings at once. A value range like "values ≤ 100" alongside a huge n points at counting/bucketing. A memory limit matters as much as time: n = 5,000 with an O(n²) DP table is 25 million cells, easily 100-200 MB in JavaScript, which tells you the DP is meant to be rolled down to one or two rows. (At n = 10⁵ the table would be 10¹⁰ cells and the time 10¹⁰ steps, so no rolling saves it — the quadratic idea itself is out.)

Read the constraints backwards Do not design a solution and then check whether it is fast enough. Read n first, derive the complexity the setter intends, and use that as a filter on which patterns are even eligible. "n ≤ 20" is not trivia — it is the interviewer telling you the answer involves subsets. Very few candidates do this, and it is visible in seconds when someone does.
Say it like this → "n is at most 20, which is a strong hint — 2²⁰ is about a million, so exponential in n is affordable, and a setter wouldn't cap n this low if a polynomial solution were intended. That points at enumerating subsets with a bitmask, most likely bitmask DP over the set of already-used elements. Let me check whether the state really is just 'which subset is used' or whether I need an index too."

Keywords to a first guess, and the check before you commit

The phrasing of a problem leaks its pattern. The table below is a list of first guesses, not answers: each phrase makes a pattern worth considering first, and the right-hand column is the one question that tells you whether the guess survives. Saying the check out loud is what separates a pattern match from a hunch.

The problem saysFirst guessCheck before you commit
"longest / shortest subarray or substring with at most k of something"Sliding windowDoes adding an element only ever push the window one way? Negative numbers break window sums.
"subarray sum equals k"Prefix sums with a hash mapAre values possibly negative? If not, a window may also work.
"sorted array", "find the pair / position"Binary search or two pointersIs the order really given, and is the condition monotonic over it?
"minimize the maximum" or "maximize the minimum"Binary search on the answerCan you write a yes/no check "is value x achievable?" that flips only once as x grows?
"k-th largest", "top k", "median of a stream"Heap (two heaps for the median)Is k small against n? If everything is needed in order, sorting may be simpler.
"next greater / smaller element", "span", "histogram"Monotonic stackDoes each element need only its nearest qualifying neighbour on one side?
"fewest steps", "shortest path" with equal-cost movesBreadth-first searchAre all moves the same cost? With weights, you need Dijkstra or worse.
"connected", "groups", "merge accounts"Union-find or a graph traversalDo you need to answer many dynamic queries (union-find) or one pass (traversal)?
"prerequisites", "order of tasks", "build order"Topological sortIs a cycle possible, and what should the answer be then?
"number of ways", "minimum cost to reach"Dynamic programmingCan you name a state that holds everything the future depends on, with small bounds?
"all combinations / permutations / partitions"BacktrackingIs n small enough that the output size is affordable? Where can you prune?
"words with a common prefix", "autocomplete"TrieAre there many queries against a fixed dictionary, so building it pays off?
"overlapping intervals", "meeting rooms"Sort by start, then sweepDo you need the sorted order, or only the maximum overlap (a sweep over events)?
"design a class with O(1) operations"A hash map composed with an ordered structureWhich two requirements pull in opposite directions?

If two rows fit, that is information, not confusion: say both guesses and use the check to choose. "This looks like a window or a prefix-sum problem; the values can be negative, so it is prefix sums" is a complete and strong sentence.

How to talk while you code

The goal is a continuous, low-effort narration that lets the interviewer follow your reasoning without interrupting. Not a play-by-play of syntax — nobody needs "now I'll write a for loop." Narrate decisions, and name your patterns, because naming is what proves the choice was deliberate rather than lucky.

Instead ofSay
silence"I'll use a monotonic decreasing stack here, because I need the next greater element for every index and a stack lets each element be pushed and popped once — amortized O(n)."
"now a map""Map from value to index rather than a Set, because I need to return indices, not just detect membership."
"hmm, hold on""I'm deciding between sorting first and using a heap. Sorting is simpler but O(n log n) up front; the heap gets me the top k in O(n log k). Since k is small I'll take the heap."
"…" while fixing a bug"That should be <=, not < — otherwise the last window never gets evaluated. Let me re-check the boundary."
"I'll handle that later""I'm deliberately deferring the empty-input case; noting it here as a TODO and I'll come back before I call this done."

Two mechanical habits pay for themselves. First, write the function signature and the return type before the body — it forces the contract to be explicit. Second, when you defer something, say so and leave a visible marker; an acknowledged gap is a plan, an unacknowledged one is a bug.

⚠ Narrating and thinking are different modes, and forcing both at once stalls people It is entirely fine to say "give me twenty seconds to think this through quietly" and then go silent — that reads as controlled. What reads badly is undeclared silence for two minutes. Buy the quiet explicitly, use it, then come back with a statement rather than a mumble.

Being stuck, handled well

You will get stuck. It is expected and it is not disqualifying — freezing is. What is being measured is whether you have a procedure for it. Work this ladder out loud, in order, and say which rung you are on.

RungDo thisWhy it works
1Re-read the constraints and the exact wording of the askMost stuckness is a misread — "subsequence" vs "subarray," "any" vs "all," "at most" vs "exactly." The constraint bound also re-states the target complexity you may have drifted from.
2Work a tiny example by hand — n = 1, n = 2, n = 3You are looking for the rule your hand is following. Solving n = 3 manually and asking "what did I just do?" recovers the recurrence more often than staring at the general case.
3Ask "what shape is this?"Not "have I seen this problem" but: is it a graph? intervals? a tree of choices? a search over a monotonic answer space? Shape recall is far more reliable than problem recall.
4Ask whether it is two known patterns composedAdvanced problems usually are. Sort + two pointers. Trie + backtracking. Binary search on the answer + a greedy feasibility check. Heap + hashmap. Topological order + DP. Say the two names out loud.
5Relax the problem, solve the easier versionDrop a constraint (assume sorted, assume no duplicates, assume k = 1, assume the array is positive). Solve that, then re-introduce the constraint and see what breaks. Partial credit is real credit.
6State where you are stuck, precisely, and take the hint"I have an O(n²) solution and I know the bottleneck is re-scanning the prefix; what I can't see is a structure that gives me the max of a shrinking window in O(1)." That is a targeted request, and it lets the interviewer give a small hint rather than a large one.
Say it like this → "I'm going to slow down for a second and work n = 3 by hand, because I think the recurrence will be obvious once I see what I'm doing manually. …Right — at each step I'm choosing between taking this element and skipping it, and the choice only depends on the remaining capacity. That's a knapsack shape, so the state is (index, remaining) and I can memoize it."

Note what that phrasing does: it converts "stuck" into "executing a deliberate step." Interviewers are instructed to give hints; taking one gracefully costs far less than most candidates fear, and refusing to ask while burning ten minutes costs far more.

Pivoting off a wrong approach without losing the interview

Sooner or later the approach you announced stops working. Candidates treat this as a catastrophe and either defend a failing idea or fall silent. It is neither: finding that an approach is wrong, early and out loud, is a strong signal, because it shows you test your own ideas. What costs points is finding out late, or finding out and hiding it.

There are three ways an approach fails, and each has a different tell.

How it failsThe tellWhat you do next
It is incorrectYou can build a small input where it gives the wrong answerKeep the counterexample on the board; it states exactly what the right idea must handle.
It is too slowThe constraints say n = 105 and your inner loop is O(n)Name the repeated work and look for the structure that removes it.
It does not fit a constraint you assumedYou relied on sorted input, positive values, or distinct elements, and the problem does not promise themAsk whether the assumption can be had cheaply (sort first?), or drop the approach.

The incorrect case deserves a worked example, because greedy is where candidates most often commit to something unproven. "Make change with the fewest coins: take the largest coin that fits, repeatedly." For some coin systems this is right. For others it is quietly wrong, and one small input shows it.

function greedyCoins(coins, amount) {
  const sorted = [...coins].sort((a, b) => b - a);
  let count = 0;
  for (const coin of sorted) {
    count += Math.floor(amount / coin);
    amount %= coin;
  }
  return amount === 0 ? count : -1;
}

function minCoins(coins, amount) {
  const best = new Array(amount + 1).fill(Infinity);
  best[0] = 0;
  for (let a = 1; a <= amount; a++) {
    for (const coin of coins) {
      if (coin <= a && best[a - coin] + 1 < best[a]) best[a] = best[a - coin] + 1;
    }
  }
  return best[amount] === Infinity ? -1 : best[amount];
}

With coins [1, 3, 4] and amount 6, greedyCoins returns 3 (it takes 4, then 1, then 1) while minCoins returns 2 (3 and 3). Amount 6 is the smallest amount where they disagree for those coins. With coins [1, 5, 10, 25] they agreed on every amount from 1 to 500, which is why the greedy habit survives: it works on the coins you grew up with.

Say it like this → "Let me test that before I code it. Take coins 1, 3 and 4 and amount 6. My greedy picks 4 then 1 and 1, three coins, but 3 and 3 is two. So the largest-first choice is not safe here. The failure is that taking the big coin can block a better combination, which means I need to consider both choices at each amount: that is a DP over amounts, one entry per amount, O(amount × coins)."

Notice the structure of that speech: the claim, the smallest counterexample, the reason it fails, and what the reason tells you to build next. Three habits make a pivot cheap. Decide on a deadline: if you cannot make an idea work by a time you set in advance (say, five minutes), it is a wrong approach until proven otherwise. Keep what survives: the parsing, the helper functions and the examples usually carry over, and saying "the input handling stays, the core changes" shows control. Say what you learned: the counterexample or the missed constraint is the first line of the next approach, not an embarrassment to skip.

Proving it correct in one sentence

"Does it work?" is asked more often than "is it fast?", and "I tested it" is not an answer to it. A one-sentence correctness argument is short enough to say aloud and strong enough to convince. It names a fact that stays true throughout the algorithm (an invariant) or a reason a discarded option cannot matter. The table gives the shape for each family; fill the blanks with the specifics of your problem.

TechniqueThe one-sentence shape
Two pointers on sorted data"If a valid pair exists, both of its indices are always inside [lo, hi], because each move discards an index that cannot be in any valid pair."
Binary search"The answer, if there is one, is always inside [lo, hi], and each step shrinks that range while keeping the answer in it."
Sliding window"The window is the longest valid one ending at right, because shrinking from the left only stops once the constraint holds again."
Greedy"Any optimal solution can be changed to start with my choice without getting worse, so committing to it loses nothing" (an exchange argument).
Dynamic programming"The state holds everything the future depends on, and the recurrence considers every way to reach it."
Breadth-first search"The queue always holds nodes in non-decreasing distance, so the first time a node is reached is by a shortest path."
Backtracking with pruning"Every complete answer is either visited or sits under a branch I cut for a stated reason that no answer can satisfy."
Monotonic stack or deque"An element is removed only when a later element makes it unable to ever be the answer."

The first row, worked once on the sorted two-sum from the follow-ups below. The loop moves lo up when nums[lo] + nums[hi] < target. Why is that safe? nums[hi] is the largest value still allowed, so nums[lo] with any partner in range is at most nums[lo] + nums[hi], which is already too small: index lo is in no valid pair, and discarding it cannot lose an answer. The other branch is the mirror image. That is the entire proof, and it also tells you the loop ends: each step discards one index.

⚠ "It passed my examples" is not a proof Examples show the code does what you think on the inputs you thought of. An invariant explains the inputs you did not think of. If you cannot state the invariant, you do not yet know why the algorithm works, and that is the same thing an interviewer's follow-up is built to find out.

Test before you say "done"

Declaring completion and letting the interviewer find the bug is the most avoidable score loss in the entire interview. Finding it yourself, out loud, converts the same bug into a positive signal. Trace the given example first — line by line, tracking real variable values, not vibes — then run a deliberately chosen edge case.

Edge caseWhat it catches
Empty input [] / ""arr[0] on an empty array, Math.max() of nothing returning -Infinity, a while loop that assumed one element
Single elementTwo-pointer and sliding-window loops whose body never executes; left < right vs left <= right
All duplicates [5,5,5,5]Dedupe logic, Set-vs-Map choices, two-pointer skip loops, "distinct" requirements
Already sorted / reverse sortedWorst-case quicksort partitioning, degenerate BSTs, and off-by-one at the array ends
Two elementsThe smallest case where a comparison or swap can be backwards
Negatives and zeroGreedy and sliding-window sum arguments that silently assume positivity; division and modulo by zero
All elements identical to the target / none matchingThe "not found" return contract you agreed on in minute two
Maximum n from the constraintsRecursion depth (JS blows up around 10⁴-10⁵ frames), integer overflow past 2⁵³, O(n²) memory
Say it like this → "Let me trace the given example before I call this done. left = 0, right = 4, sum = 9, target is 9 — returns [0, 4], matches. Now an edge case: single element. The while (left < right) never executes, so I fall through to the not-found return — correct. And an empty array: nums.length - 1 is -1, the loop still doesn't execute, still correct. I'm happy with this."
⚠ Tracing "in your head" is not tracing Under pressure, silently re-reading code confirms what you intended to write, not what you wrote. Say the actual variable values out loud, or write them in a comment block. The whole value of the exercise comes from forcing yourself to evaluate rather than recognize — and it is also the only way the interviewer can see you doing it.

Follow-ups: it is a stream, it is sorted, it does not fit on one machine

The first solution is rarely the end. Interviewers keep the same problem and change one assumption, and each change has a standard move. Hearing the change as "which resource just got taken away?" is what turns the follow-up from a surprise into a lookup.

The follow-upWhat was taken awayThe move
"The data arrives as a stream."Random access, a second pass, and the right to store everythingKeep a small summary that one pass can update: a counter, a heap of size k, a sample.
"The input is already sorted."Nothing: you are being given somethingDrop the hash map or the sort. Two pointers or binary search now do it in O(1) extra space.
"There are many queries on the same data."The right to pay O(n) per questionPreprocess once: prefix sums, a sorted copy, an index.
"It does not fit in memory."Holding the whole inputProcess in chunks and merge partial results (external sort, counting per chunk).
"It is spread over many machines."One place to computeCompute locally, send a small summary, combine. The question is whether the answer can be combined from the pieces' answers.

Streaming: one pass, constant memory

A typical case: find the element that occurs more than half the time in a stream, without storing the stream. Keep a candidate and a counter. A matching element adds a vote; a different one cancels a vote; when the votes hit zero the next element becomes the candidate. If a strict majority exists, it cannot be cancelled out, because every cancellation removes one copy of it together with one copy of something else.

class MajorityStream {
  constructor() {
    this.candidate = undefined;
    this.count = 0;
  }

  add(x) {
    if (this.count === 0) {
      this.candidate = x; // nothing is standing: x takes the floor
      this.count = 1;
    } else if (x === this.candidate) {
      this.count++;
    } else {
      this.count--; // x cancels one vote for the candidate
    }
  }
}

The guarantee is conditional, and saying so is the point. If a strict majority exists, candidate is that value: I tested 3,000 random streams that each contained one. If no majority exists, the candidate is just whatever survived: after adding 1, 2, 3 it is 3 with a count of 1, and a second pass is the only way to confirm. A stream gives you no second pass, so the honest statement is "this tells you the majority if there is one".

Sorted input: the hash map was only buying you order

For two-sum, the hash-map solution is O(n) time and O(n) space. Given sorted input, the same question needs no extra memory.

function twoSumSorted(nums, target) {
  let lo = 0;
  let hi = nums.length - 1;
  while (lo < hi) {
    const sum = nums[lo] + nums[hi];
    if (sum === target) return [lo, hi];
    if (sum < target) lo++; // nums[lo] is too small even with the largest partner left
    else hi--; // nums[hi] is too large even with the smallest partner left
  }
  return [];
}

Time O(n), space O(1). It returns the pair's positions, or an empty array when there is none; I checked it against all pairs on 3,000 random sorted arrays, including empty ones. The follow-up lesson is general: a sorted input is a free resource, and a solution that ignores it is probably using more memory than it needs.

Distributed: can the answer be combined from the pieces?

For "the k largest of data spread over many machines", ask whether the global answer can be assembled from each machine's local answer. It can: every element of the global top k is also in the top k of the machine that holds it, so each machine sends only its own best k and one coordinator takes the best k of those.

function mergeTopK(shards, k) {
  const candidates = shards.flatMap((shard) => [...shard].sort((a, b) => b - a).slice(0, k)); // each machine sends only its own best k
  return candidates.sort((a, b) => b - a).slice(0, k);
}

The coordinator handles at most k times the number of machines items, however large the data is. I compared it with sorting everything on 2,000 random splits. Not every question combines this way: the median of the whole is not derived from the medians of the parts, so a distributed median needs a different design (counting values in ranges, then narrowing). Saying which kind of question you have is the senior answer.

Discussing tradeoffs at a senior level

The clearest seniority marker in the last ten minutes is raising a tradeoff before you are asked, and framing it against a use case rather than in the abstract. Intermediate candidates report complexity. Advanced candidates report complexity, name the alternative they rejected, and say what would change their mind.

AxisThe sentence to have ready
Time vs space"This is O(n) time with an O(n) hashmap. If memory were the binding constraint I'd sort in place and use two pointers — O(1) extra space, O(n log n) time. Which matters more here?"
Preprocess vs per-query"If this is called once, the linear scan is right. If it's called a million times on the same array, I'd build prefix sums up front — O(n) once, then O(1) per query."
Worst case vs average case"Quickselect is O(n) expected but O(n²) adversarially. If this is on a user-facing path where input could be hostile, I'd take the heap's guaranteed O(n log k) instead."
Simplicity vs constant factor"The bitmask version is maybe 5× faster per node but noticeably harder to read. For n ≤ 12 I'd ship the readable one and leave a comment about the optimization."
Amortized vs bounded latency"The dynamic array is amortized O(1) but a single resize is O(n). If this were in a real-time path I'd pre-size it."
Exact vs approximate"For exact distinct counts I need O(n) memory. If an error of a percent or two is acceptable at this scale, HyperLogLog does it in kilobytes."
Mutating vs pure"I'm sorting the input in place, which is faster but destroys the caller's array. If it's shared, I'd copy first and pay the O(n)."

Every one of those ends in a question or a condition. That is deliberate — it turns a monologue into a design conversation and invites the interviewer to supply real-world context, which is the exact interaction they are trying to score. It is also honest: which side of a tradeoff is correct genuinely depends on information you do not have.

Two more things to have ready without being asked. How would this scale past one machine? — even a sentence ("if the array doesn't fit in memory, I'd external-sort by chunk, or shard by hash of the key and merge") shows the thinking extends past the whiteboard. And what would you test? — naming three unit tests and one property ("the output should always be a permutation of the input") signals engineering maturity that pure DSA never reaches.

When the problem is NP-hard: recognise it, then say the right thing

Some problems have no known algorithm that stays fast as n grows, and an interviewer will occasionally give you one on purpose. Failing to notice is the costly mistake: you spend thirty minutes hunting a polynomial solution that, as far as anyone knows, does not exist. Noticing is cheap, because the disguises are few.

It sounds likeIt isWhen n is smallWhen n is large
"Visit every city once, shortest tour"Travelling salesmanBitmask DP over visited sets, O(n2 · 2n)Heuristics or an approximation (for distances that obey the triangle inequality, a tour at most twice optimal from a spanning tree)
"A subset that adds up to exactly T"Subset sumMeet in the middle, O(2n/2 · n)If the values are small, a DP over sums: pseudo-polynomial in T, not in n alone
"Split into k groups of equal total"Partition and bin packingBucket backtracking with symmetry breaking, or bitmask DPGreedy heuristics such as largest-first
"Fewest sets that cover everything"Set coverBacktracking with a bound, or bitmask DPRepeatedly take the set that covers the most uncovered items (a known approximation)
"Longest simple path in a general graph"Longest pathBacktracking or bitmask DPNo efficient exact method is known

The clue that gives it away is almost always in the constraints: a bound like n <= 20, or n <= 40 with a sum question, is the setter telling you an exponential method is intended. The numbers decide how exponential. Counting steps against a budget of 108 operations, the largest n that fits is:

CostLargest n within 108Cost at that n
n!1139,916,800
3n1643,046,721
n2 · 2n1884,934,656
n · 2n2292,274,688
2n2667,108,864

Computed with a loop over n, so the cutoffs are exact for that budget. The table also explains the odd bounds: meet in the middle turns 240 into about 2 × 220 sums, which is why subset-sum questions go up to n = 40 while bitmask DP questions stop near 20.

Many problems look like the hard ones and are not, and mixing them up is the opposite error. The nearby pairs worth knowing: the shortest path is easy and the longest simple path in a general graph is hard (in a directed acyclic graph the longest path is easy again, by dynamic programming over a topological order); a tour using every edge once (Eulerian) is easy and a tour visiting every vertex once (Hamiltonian) is hard; deciding whether a graph can be coloured with two colours is easy and with three is hard.

Say it like this → "This is subset sum in disguise, which is NP-hard in general, so I'm not going to look for a polynomial algorithm. The constraint is n up to 40, so an exponential method is the intended one, and 240 is too many but 220 is fine: I'll split the items in two halves, list each half's sums, and match them. If the values were small instead, I'd use a DP over the sum."
⚠ What not to say "It's NP-hard, so it can't be done." It can, within the size the constraints give. "I'll find a polynomial algorithm." Nobody knows how for these problems, and promising one reads as not knowing the landscape. Claiming a problem is NP-hard when you are not sure. Say "this reminds me of" instead, and name the problem; the check is whether you can see a clean reduction, and the near-miss pairs above show how often it fails.

A full mock: one Hard problem, start to finish

Everything above is easier to believe once you have watched it run on a real problem. Below is an interview on Sliding Window Maximum, written as a transcript. The notes in italics say which behaviour is being scored. Every piece of code shown was run: the brute force and the final function were compared on 3,000 random arrays, the trace table is the real output, and the wrong draft really returns what is claimed.

Interviewer: Given an integer array nums and a window size k, the window slides one position at a time from the left end to the right end. Return the maximum of each window.

You: Let me restate it: for every run of k consecutive elements, output the largest, so there are n - k + 1 answers. A few questions that change my approach. How large can n be? Can values be negative? Is k always between 1 and n?

Interviewer: n up to 105, values can be negative, 1 <= k <= n.

Scored: three questions, each tied to a decision (target complexity, sign of values, an empty-window case that cannot occur).

You: Let me do the example by hand. For [1, 3, -1, -3, 5, 3, 6, 7] with k = 3, the windows are [1,3,-1], [3,-1,-3], [-1,-3,5], [-3,5,3], [5,3,6], [3,6,7], so the answer is [3, 3, 5, 5, 6, 7]. Brute force: for each window, scan its k elements.

function maxSlidingWindowBrute(nums, k) {
  const result = [];
  for (let i = 0; i + k <= nums.length; i++) {
    let max = nums[i];
    for (let j = i + 1; j < i + k; j++) if (nums[j] > max) max = nums[j];
    result.push(max);
  }
  return result;
}

You: That is O(n · k). With n = 105 and k = 50,000 that is about 50,000 windows times 50,000 comparisons, roughly 2.5 billion, far past the 108 a second I can count on. The bottleneck is that consecutive windows share k - 1 elements and I rescan them every time.

Scored: brute force with its complexity, the number checked against the constraint, the bottleneck named in one sentence.

You: First idea: remember the current maximum and only rescan when the element leaving the window is that maximum. Most windows are then O(1). Hold on, a decreasing array breaks it. In [8, 7, 6, 5, ...] the maximum is always the element that just left, so I rescan on every window. I counted it: on 20 decreasing numbers with k = 5 it rescans 15 times, on every window after the first, 75 comparisons; on 10,000 numbers with k = 5,000 it does 25 million comparisons. That is the brute force again in its worst case, so I'm dropping it.

Scored: a pivot. The idea was tested on an adversarial input before any code was written, and the counterexample was kept.

You: What the failed idea shows is that after the maximum leaves, I need the next candidate ready. A max-heap of (value, index) pairs does it: pop the top while its index is outside the window. That is O(n log n) with lazy deletion and would pass. But look at what the heap keeps that it never needs: if nums[j] <= nums[i] and j < i, then j can never be a window maximum again, because i is both larger and will stay in the window longer. So I can throw such a j away immediately. What remains is a list of indices whose values strictly decrease, oldest first. The front is the maximum; the front expires when it leaves the window; a new element removes smaller ones from the back. That is a monotonic deque, and each index is pushed once and removed at most once, so O(n).

Scored: the optimization is a derivation from the bottleneck, an alternative (heap) is named with its cost, and the key fact ("can never be a maximum again") is stated as the reason.

Interviewer: Go ahead and code the deque version.

You: Signature first: takes nums and k, returns an array. I'll use an array plus a head index rather than shift(), which is O(n).

function maxSlidingWindow(nums, k) {
  const result = [];
  const deque = []; // indices; their values strictly decrease from front to back
  let head = 0; // deque[head] is the front; moving head forward is a pop-front without shift()
  for (let i = 0; i < nums.length; i++) {
    if (head < deque.length && deque[head] <= i - k) head++; // the front slid out of the window
    while (deque.length > head && nums[deque[deque.length - 1]] <= nums[i]) deque.pop(); // smaller or equal values can never be a max again
    deque.push(i);
    if (i >= k - 1) result.push(nums[deque[head]]);
  }
  return result;
}

Scored: narrating decisions (head index versus shift(), why <= pops equal values) instead of syntax.

You: Now I trace the example, with real values. The table shows the deque after each step as index:value.

inums[i]deque after the stepoutput
010:1-
131:3 (the 1 is smaller, popped)-
2-11:3, 2:-13
3-31:3, 2:-1, 3:-33
454:5 (index 1 expired, 2 and 3 popped)5
534:5, 5:35
666:66
777:77

You: That gives [3, 3, 5, 5, 6, 7], matching my hand answer. Now edge cases. k = 1 should return the array itself. Take [1, 0]. I expect [1, 0] and ... my first draft of the expiry line was deque[head] < i - k, and it returns [1, 1]. That is an off-by-one: the window holds indices i - k + 1 to i, so an index equal to i - k is already out and the test must be <=. The final code above has the fix. It is worth knowing that the buggy draft produces the right answer on the given example, so the example alone would not have caught it.

Scored: tracing with values, then choosing an edge case that probes the boundary, and finding the bug before the interviewer does.

You: Complexity: each index is pushed once and popped at most once, so O(n) time. The live part of the deque never holds more than k indices; this array-and-head version does not free the consumed front, so its memory is O(n), and a circular buffer would make it O(k). Correctness in one sentence: the deque always holds, in order, exactly the indices in the window that are not dominated by a later index, so its front is the window's maximum. I also compared it with the brute force on 3,000 random arrays with negatives and repeated values, plus k = 1 and k = n.

Interviewer: What if the numbers arrive as a stream and you must report a maximum after each arrival?

You: The same code works almost unchanged: the loop body already handles one element at a time and only needs the indices it has seen, so I would keep the deque and a running index as fields and emit the front after each push once k elements have arrived. With a circular buffer, memory stays O(k). If the window were defined by time rather than by count, the expiry test would compare timestamps instead of indices; the structure is the same.

Scored: a follow-up absorbed by naming what changed (nothing about the algorithm, only what the front is compared against).

What the transcript did, in order
  • Questions that each changed the plan, then the example answered by hand.
  • Brute force with its complexity, checked against the constraint, bottleneck named.
  • A first improvement tested on an adversarial input and dropped before any code.
  • The real idea derived from what the failure showed, with the alternative's cost stated.
  • Code narrated, traced with values, an edge case found a bug, the fix explained.
  • Complexity, a one-sentence correctness argument, then the follow-up answered by saying what changes.

Signals that separate an advanced performance from an intermediate one

  • Constraints are read as a hint, not as trivia. "n ≤ 20, so they intend an exponential-in-n solution — that means bitmask" is said in the first two minutes, not discovered at minute thirty.
  • A brute force is stated with its complexity before any optimizing starts. There is always something on the board, and the improvement is measured against a named baseline rather than asserted.
  • Patterns are named out loud as they are chosen. "Monotonic stack, because I need the next greater element and each index is pushed and popped once" — not just correct code that happens to be a monotonic stack.
  • The bottleneck is identified explicitly ("the expensive part is re-scanning the prefix") and the optimization is presented as buying that specific work back with a specific structure.
  • Getting stuck produces a described procedure, not silence. Re-read constraints → hand-trace a tiny case → identify the shape → check for two composed patterns → relax a constraint → ask a precise question.
  • The candidate tests their own code before declaring done — a real trace with real values, plus a deliberately chosen edge case, and finds their own off-by-one.
  • Tradeoffs are raised unprompted and tied to a use case, with a stated condition that would flip the decision, rather than a memorized complexity table recited on request.
  • A wrong approach is abandoned early and out loud. The counterexample or the missed constraint is stated, kept on the board, and used as the first line of the next approach.
  • Correctness is claimed with an invariant, not with examples. One sentence says what stays true throughout, or why a discarded option cannot matter.
  • Hard problems are recognised as hard. When the constraints point at an exponential method, the candidate says which problem it resembles and works within the stated size instead of hunting for a polynomial algorithm.
  • Corrections are absorbed without defensiveness. A hint is taken, integrated, and credited — "good catch, that breaks when the values are negative; let me fix the invariant" — because the interviewer is simulating what code review with you feels like.
  • Uncertainty is stated honestly and bounded. "I'm fairly sure this is O(n log n) amortized but I'd want to double-check the resize cost" beats a confident wrong claim every time, and it is the difference between an engineer you can trust and one you have to verify.
Before you move on
  • Run the seven-step framework out loud on a problem you have never seen, including stating a brute force first.
  • Read a constraint like n ≤ 10⁵ and say, before designing anything, which complexity class it's steering you toward.
  • Name the bottleneck in a brute-force solution in one sentence, before naming the data structure that fixes it.
  • Describe your procedure for being stuck, rather than going silent, and rehearse saying it out loud.
  • Test your own solution against the original example plus one edge case before declaring it done.
  • Say, for each checkpoint at minutes 5, 12, 25, 33 and 40, what must already be true and what you do if it is not.
  • Take a keyword from a problem statement, name the first-guess pattern, and say the one check that would make you abandon it.
  • Find a counterexample to a greedy idea on a tiny input, and turn it into the first line of your next approach.
  • Write the one-sentence correctness argument for two pointers on sorted data, and for a monotonic deque.
  • Given "it is a stream", "it is sorted" or "it is distributed", say which resource was taken away and what the standard move is.
  • Look at a constraint like n <= 40 with a subset-sum question and say which exponential method is intended and why.

07Practice this chapter

Opens in the editor: write it, run it, and check it against real tests.

  • 8 testsRead the Constraint, Pick the Complexityintermediate
  • 8 testsWhich Approaches Actually Fit?intermediate
  • 8 testsHow Big Can n Be?intermediate
  • 8 testsBreak the Greedy Ideaintermediate
  • 8 testsMajority Element in a Streamadvanced
  • 9 testsSliding Window Maximumadvanced
  • 8 testsCoin Changeintermediate
All 159 for this level →

✓Check yourself

5 questions, pass at 4. The check runs in your browser and the result stays on this device, so it is a self-check and not a certificate.

A14

Finished reading?

0 of 48 chapters read in this series

Mark as read

→Next stop

  1. A13Previous · A13Sweep line, offline queries and geometry
  2. A14you are here
  3. The endBack to levels and planAll chapters

01On this page

16 sections · 0%

  1. 01What is actually being scored
  2. 02The 45 minutes as checkpoints, not hopes
  3. 03The first five minutes: questions that change the answer
  4. 04A framework that works on a problem you have never seen
  5. 05Constraints → intended complexity: the highest-leverage table here
  6. 06Keywords to a first guess, and the check before you commit
  7. 07How to talk while you code
  8. 08Being stuck, handled well
  9. 09Pivoting off a wrong approach without losing the interview
  10. 10Proving it correct in one sentence
  11. 11Test before you say "done"
  12. 12Follow-ups: it is a stream, it is sorted, it does not fit on one machine
  13. 13Discussing tradeoffs at a senior level
  14. 14When the problem is NP-hard: recognise it, then say the right thing
  15. 15A full mock: one Hard problem, start to finish
  16. 16Signals that separate an advanced performance from an intermediate one
Mark as read

[] chapters · t top