AI EngineeringZero to ProductionHome·About·What’s new·Contact
Career & Interview Prep · Chapter CR3

The coding interview

Not the problems (that's DSA) but the process interviewers grade: clarify→approach→code→test→complexity, thinking aloud, and recovering when stuck — with a solve-and-self-test example.

⏱️ ~2 hours🧪 2 labs🎯 Beginner→Tech-lead

Learning objectives

  • Run the coding-interview process, not just solve the problem.
  • Think aloud and communicate your approach.
  • Test your solution and state complexity.
  • Recover gracefully when stuck; coach others through it.
▶ Runnable companionThe tools here are saved under code/cr3-coding-interview/ — run them against your own resume, stories, and offers.

1 · The process beats the answer essential

Here's the failure almost everyone lives once. You get a problem you actually know. You see the answer in your head, so you put your head down and type it out in silence, fast and correct. You hand it over feeling great — and the rejection email lands two days later. What happened? You solved the puzzle but never showed the interviewer how you got there, and the whole point of the hour was to watch you think. A correct answer with no visible reasoning reads, to the person grading you, almost exactly like a memorized answer you can't repeat on a harder question.

Think of it like a maths exam that says "show your work." The final number is worth almost nothing; the marks are in the steps. A coding interview is the same contract — except the "work" isn't just algebra, it's the engineering conversation: what did you ask before assuming, what trade-off did you weigh, how did you check you were right. The interviewer is a stand-in for your future teammate, and they're answering one quiet question the entire time: would I want this person debugging a production incident next to me at 2am?

So the precise idea this section teaches: a coding interview grades a repeatable process, not a single right answer. That process is five visible steps — clarify → approach → code → test → complexity — and each one is a signal the interviewer is explicitly listening for. Clarifying shows you don't build the wrong thing confidently. Stating an approach first shows you plan before you type. Narrating while you code shows your reasoning is sound even when your syntax slips. Testing shows you don't trust your own code blindly. Stating complexity shows you know what your solution actually costs at scale.

The most common mistake — and it's the one that sinks strong programmers — is treating those steps as ceremony to rush past on the way to the "real" work of coding. They are the real work here. A candidate who talks through a clean brute force, tests it, and names a path to optimize routinely out-scores one who silently produces a slicker solution, because the first candidate demonstrated the exact behaviours the job needs and the second left the interviewer guessing. Master the loop below and you turn interviewing from a lottery of "did I happen to see the trick" into a skill you can reliably execute — even on problems you've never met.

The DSA track taught you to solve the problems. This is the process interviewers actually grade: how you clarify, communicate, and verify — many candidates who reach the answer still fail on process.

Clarify ask questions Approach before coding Code think aloud Test edge cases Complexity big-O
🗺️ How to read this diagram

This strip is the process a coding interview grades — not the puzzle itself, but the five steps you should visibly walk through every time. Read the boxes left to right; the small text under each is what that step involves.

  • Clarify — before touching code, restate the problem and ask questions about inputs, edge cases and limits. Jumping straight to code is the classic mistake.
  • Approach — say your plan out loud before coding and get the interviewer's nod. This is where you mention the trade-off ("brute force is O(n²), a hash map is O(n)").
  • Code — write it cleanly while thinking aloud so the interviewer follows your reasoning, not just your keystrokes.
  • Test — trace an example and the edge cases (empty, one item, duplicates) and fix bugs you spot. Complexity — finish by stating the big-O time and space. The arrows mean you move through them in this order.

In short: Reaching the right answer isn't enough — interviewers hire on this loop. Practise narrating all five steps aloud, because silence and skipping straight to code are what actually sink candidates.

The 5 steps, every time

  1. Clarify: restate the problem, ask about inputs/edge cases/constraints. Never code immediately.
  2. Approach: propose an approach out loud, state its complexity, get a nod before coding.
  3. Code: implement cleanly, narrating as you go.
  4. Test: walk through an example + edge cases; fix bugs you find.
  5. Complexity: state time/space, and whether you could do better.

2 · Think aloud essential

Picture the two minutes that decide the interview. You've read the problem, you're staring at the whiteboard, and your mind is churning through ideas — but from the outside, the interviewer sees a person frozen, saying nothing, for a full silent minute. They can't tell if you're brilliantly working toward the optimal approach or completely lost. And here's the cruel part: because they can't tell, they usually assume the worse of the two. Silence doesn't read as "deep thought"; it reads as "stuck and hiding it."

The fix is a habit that feels unnatural at first: narrate your thinking out loud, continuously, as if you were pair-programming with a colleague who can only hear you, not read your mind. Say the idea before it's polished. Say the trade-off you're weighing. Say the thing you just rejected and why. Compare it to a surgeon in a teaching hospital talking through every cut — the value isn't just the operation, it's making the invisible reasoning visible so it can be followed and trusted.

Why this earns its own section rather than being a throwaway tip: thinking aloud is not decoration on top of solving — it is most of what is being measured. The interviewer is building a model of how you reason, and that model is made entirely of what you say. A sentence like "I could brute force this in O(n²) with nested loops, but if I trade some memory for a hash map I get O(n) — let me do that" does three jobs at once: it shows you know multiple approaches, that you reason about cost, and that you choose deliberately. That single narrated sentence can be worth more than ten lines of correct code.

The mistake to name plainly: going quiet exactly when it's hardest, because that's when you feel most exposed. Counterintuitively, the stuck moments are when narration matters most — "Okay, my first idea doesn't handle duplicates, so let me think about what structure would" keeps the interviewer with you and often lets them drop a hint, whereas silence gives them nothing to help with. Talk through your options even when you don't have the answer yet; a candidate reasoning aloud toward a solution beats one who arrives silently at it. If narrating feels awkward, that's normal — it's a muscle, and the practice ladder and mock-interview exercise later in this page exist to build it until it's automatic.

Silent candidate 🤐 thinking, says nothing interviewer can't tell brilliant? or lost? assumed: stuck Narrating candidate 🗣️ "O(n²) → hash map O(n)" reasoning made visible: knows options · cost · chooses gradeable signal Narration is the thing being measured. Silence gives the interviewer nothing to grade, so they assume the worse; one narrated trade-off sentence shows knowledge, cost-awareness, and deliberate choice at once.
🗺️ How to read this diagram

Two candidates, same head full of ideas — read the left column top-to-bottom, then the right. Both are thinking; only one lets the interviewer see it.

  • The left (dashed, red) path is the silent candidate: real thinking happens in the top box, but nothing comes out, so the interviewer's box below can't distinguish "brilliant" from "lost" — and defaults to the red conclusion assumed: stuck.
  • The right (solid blue) path is the narrating candidate: the very same reasoning is spoken aloud ("brute force O(n²), a hash map gets O(n)"), so the interviewer's box turns green — the invisible reasoning became visible.
  • The payoff is the bottom labels: identical thinking, opposite outcomes. The single narrated sentence proves three things — you know multiple approaches, you reason about cost, and you choose on purpose.

In short: The interviewer can only grade what you say out loud. Silence reads as "stuck"; narration converts your thinking into the signal you're actually scored on.

Silence is the killer. Interviewers hire for how you think, so narrate: "I could brute force this O(n²), but a hash map gets O(n) — let me do that." Even when stuck, voice your options.

3 · Test & state complexity intermediate

Watch the moment that quietly costs candidates the offer. You finish typing, lean back, and say the fatal word: "Done." The interviewer says, "Are you sure? What happens on an empty list?" — and you realize, in front of them, that your code crashes on []. You didn't fail because of the bug; every real codebase has bugs. You failed because you declared victory without checking, and that tells your future teammate you'll push code to production the same way. The candidate who says "let me trace a couple of cases before I call it done" and catches their own bug looks strictly stronger than the one who needed to be caught.

So this section teaches two disciplines that a whiteboard tempts you to skip but the job never lets you: testing your own solution and stating its complexity. Treat testing like a pilot's pre-landing checklist — the plane usually lands fine, but you run the list every single time because the one time you skip it is the time it matters. Before you say "done," you walk through: a normal input, then the edges that break naive code — an empty input, a single element, duplicates, and the largest/smallest values. Trace them by hand or, better, write quick assert lines that pin the behaviour down so a failure stops you loudly instead of passing silently.

The second discipline, complexity, is you answering the question "what does this cost at scale?" before the interviewer has to ask it. State the time and space in Big-O — the analysis skill from DS1 — and, crucially, say whether you think you could do better. "This is O(n) time and O(n) space; I don't think you can beat linear time since every element must be read" shows a maturity that a bare "it works" never will. Volunteering the cost turns a passive candidate into one who owns their engineering decisions.

The mistake to avoid is treating tests and complexity as an afterthought you tack on if there's time left — or worse, waiting to be prompted for them. Offer both unasked. The two-sum example below is deliberately built the way you should build live: the solution, its own asserts covering normal, duplicate, and no-answer cases, and a stated complexity, all in one piece. Read it as a template for the habit, not just for the algorithm — the algorithm is the easy part; the surrounding rigor is what's actually being graded.

draft solution "looks right" the checklist normal input empty [ ] single item dupes · min/max ✓ state big-O time + space, and "could I do better?" skipping straight to "Done." ✗the bug you didn't catch, they will Never say "Done" first. Run the edge-case checklist (empty, single, duplicates, extremes), then volunteer the time/space Big-O unasked — catching your own bug reads far stronger than being caught.
🗺️ How to read this diagram

Follow the blue arrows left to right — this is the gap between "I think it works" and "I verified it works," which is exactly what this step grades.

  • The left box is your draft that merely looks right — the tempting place to stop.
  • The middle stack of green rows is the checklist you run every time before claiming done: a normal input, then the edges that break naive code — empty, a single item, duplicates, and min/max extremes.
  • The right green box is the second discipline: after testing, volunteer the Big-O time and space and whether you could beat it — unprompted.
  • The red bar underneath is the shortcut to avoid: jumping from draft straight to "Done." skips the checklist, and the bug you didn't catch is the one the interviewer will.

In short: Draft → checklist → stated complexity, every time. Testing yourself and owning the cost turns "it works" into "I would merge this."

Before saying "done," trace an example and the edges (empty, one element, duplicates). Then give big-O — the analysis skill from DS1. Here's a clean solution with its own test, the way you should verify live.

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
Python · solve + self-test + complexity (runs)
two_sum.pydef two_sum(nums, target):
    """Return indices of two numbers summing to target. O(n) time, O(n) space."""
    seen = {}                              # value -> index
    for i, n in enumerate(nums):
        if target - n in seen:
            return [seen[target - n], i]
        seen[n] = i
    return []

# TEST like you would out loud in the interview:
assert two_sum([2, 7, 11, 15], 9) == [0, 1]     # normal
assert two_sum([3, 3], 6) == [0, 1]             # duplicates
assert two_sum([1, 2], 10) == []               # no answer (edge)
print("all cases pass — O(n) time, O(n) space")
all cases pass — O(n) time, O(n) space
▶ How this works

This is a model of what to produce live: a clean solution to the classic "two-sum" problem (find the two numbers that add up to a target) plus its own tests and a stated complexity. It shows the whole Test + Complexity habit, not just the algorithm.

  1. The line in triple quotes right under def is a docstring — a short note saying what the function does and its cost (O(n) time and space). Stating cost up front is exactly what interviewers want.
  2. seen = {} is an empty dictionary that will remember each number and the position (index) where we saw it. for i, n in enumerate(nums) loops over the list giving both the index i and the value n.
  3. if target - n in seen: is the trick: for the current number, we check whether the number we'd need to reach the target has already been seen. If so, we return the two positions. Otherwise seen[n] = i records this number for later.
  4. Below the function, the three assert lines are tests — each says "this must be true or stop". They cover a normal case, duplicates, and a no-answer edge case, mirroring how you'd verify aloud in the room.

What the output means: all cases pass — O(n) time, O(n) space prints only if all three assert checks held. If any assertion failed, Python would stop with an AssertionError pointing at the broken case.

Try this: Add one more assert for an empty list — two_sum([], 5) == [] — and re-run. Writing a failing edge case first, then confirming it passes, is the habit that scores points.

4 · Advanced — recovering when stuck advanced

It will happen to you, and it happens to strong engineers too. Twenty minutes in, the clever approach you were sure would come… doesn't. Your pulse climbs, your mind goes blank, and the temptation is to stare at the board in humiliated silence hoping the trick materializes. Here's the reframe that changes everything: interviewers expect you to get stuck — many problems are designed so you will — and they are not grading whether you get stuck. They're grading what you do next. Getting stuck isn't the test failing; it's the test starting.

Think of it like a hiker who loses the trail. The panicked hiker thrashes deeper into the woods and makes things worse. The trained one stops, gets their bearings from a known landmark, and walks back to solid ground before pushing forward again. Recovery in an interview is that same move: instead of chasing the elusive optimal solution, you retreat to something you know is correct — even if it's slow — and advance from there on firm footing. A working brute force is your known landmark.

The concrete recovery script has a few reliable moves, and saying them out loud is the whole point. State what you know: "The answer clearly involves checking pairs, so a nested-loop brute force would work — it's O(n²), but it's correct." Try a smaller example by hand; shrinking the input from ten items to three often makes the pattern visible. Name the wasted work in the brute force ("I'm re-scanning the same range every time") because the inefficiency you can name is usually the exact thing the optimization removes. Or ask a targeted question — not "I'm stuck," but "would you expect the input to be sorted?", which sometimes unlocks the intended trick.

The mistake this section exists to prevent is the two failure modes that actually sink candidates: panicking (flailing between half-ideas, erasing working code) and going silent (giving the interviewer nothing to help with and nothing to grade). Both read as "can't handle being stuck," which is a terrible signal for a job that is mostly being stuck. Methodical recovery, narrated aloud, can pass an interview even when you never reach the elegant answer — because it demonstrates precisely the resilience the role demands. The callout below states the rule this all builds to: a tested, working simple solution plus a clear path to better beats a half-finished clever one, every time.

😰 stuck optimal won't come recover methodically say what you know ·shrink the example panic / go silent flail · erase · freeze working brute force O(n²) tested →"now optimize" can still pass ✓ fails ✗ Getting stuck isn't graded — the branch you take is. Retreat to a correct brute force and narrate the path to better (green) and you can still pass; panic or silence (red) is what actually sinks the round.
🗺️ How to read this diagram

Start at the amber "stuck" box on the left — the fork in the middle is the whole lesson: from the same stuck moment, two very different paths lead out.

  • The blue (upper) arrow is methodical recovery: state what you know, shrink the example, then retreat to a correct brute force — even an O(n²) one — get it tested, and say "now let me optimize." That path ends green: can still pass ✓.
  • The red (lower) arrow is panic or silence — flailing between half-ideas, erasing working code, or freezing. It gives the interviewer nothing to grade and ends red: fails ✗.
  • The payoff element is that the two branches leave from the identical starting box: interviewers expect you to get stuck, so they aren't grading the stuck moment — they're grading which arrow you follow.

In short: A tested brute force plus a narrated path to better beats a half-finished clever attempt. Recover out loud; never freeze.

Getting stuck is normal; how you recover is graded. Say what you know, try a smaller example, state a brute force and improve from there, or ask a targeted question. Panicking or going silent fails; methodical recovery can still pass.

A working brute force beats a broken 'optimal'If the elegant solution isn't coming, code the O(n²) one, get it working and tested, then say "now let me optimize." A correct simple answer + clear path to better often scores higher than a half-finished clever one.

5 · Professional — the remote/practical variants professional

Here's a trap that catches good algorithmic candidates off guard. They ace the whiteboard rounds, get sent a take-home project, and submit a single file with a working function and nothing else — no tests, no README, a cryptic variable named x, and a crash on the first malformed input. They're baffled by the rejection: "but the algorithm was correct!" The problem is they answered the take-home as if it were a whiteboard puzzle, when a take-home is grading something entirely different — whether the interviewer would merge your code into their repo.

Modern hiring has moved well beyond the standing-at-a-whiteboard format, and each variant grades a different slice of the job. A take-home is a miniature of real engineering: you have time and a real editor, so the bar rises to production standards — clear structure, a docstring, input validation, tests, a note on assumptions and complexity. Treat it exactly like code you'd open a pull request for (the DF and TQ tracks are the standard). A pair-programming round drops you into a shared editor with the interviewer as collaborator, not examiner — here they're watching whether you communicate, ask when unsure, and actually use your tools (docs, a REPL, running the code) rather than performing from memory. A debugging round hands you broken code and grades the systematic loop from TQ4: reproduce the failure first, isolate it, then fix — never guess-and-poke.

The unifying idea is that these formats deliberately test your everyday engineering habits instead of your ability to reinvent an algorithm under pressure. That's usually good news: the habits are more controllable than whiteboard luck, and they're the ones you're building across this whole course. The mistake to avoid is applying whiteboard instincts to a non-whiteboard round — dumping a bare function into a take-home, coding silently through a pair round as if being observed, or randomly mutating a debugging problem instead of reproducing it methodically. Match your behaviour to the format, and let your working habits — clean commits, tests, clear communication — be the differentiator that a raw algorithm never is.

same job, three formats · each grades a different slice Take-home time + a real editor structure · docs validation · tests note complexity "would I merge this?" Pair-programming shared editor · collaborator communicate ask when unsure use docs · REPL not from memory Debugging broken code handed to you reproduce first isolate the fault then fix never guess-and-poke Match your behaviour to the format. A take-home grades production habits, a pair round grades communication and tool use, a debugging round grades a systematic reproduce→isolate→fix loop — whiteboard instincts fail all three.
🗺️ How to read this diagram

Three side-by-side panels, one per modern interview format — scan each column top to bottom to see what that round is actually grading.

  • The left panel (Take-home) gives you time and a real editor, so the bar rises to production code: readable structure, a docstring, input validation, tests, a complexity note — the payoff line is "would I merge this?".
  • The middle panel (Pair-programming) is collaborative, not an exam: the green rows are communicating, asking when unsure, and actually using your docs and REPL — not performing from memory.
  • The right panel (Debugging) hands you broken code and grades a systematic loop — reproduce the failure first, isolate it, then fix — with "never guess-and-poke" as the warning.
  • The grouping is the point: the same job, sliced three ways. Applying whiteboard instincts (a bare function, silent coding, random mutation) fails whichever slice you're in.

In short: Read which format you're in and switch habits: production rigor for take-homes, communication and tool-use for pairing, reproduce-isolate-fix for debugging.

Beyond whiteboard algorithms: take-home projects (treat like real code — tests, README, clean commits: DF/TQ!), pair-programming rounds (collaborate, ask, use the tools), and debugging rounds (reproduce, isolate, fix — TQ4's bug-fix-first). Your engineering habits are the differentiator here.

6 · Tech-lead — the interviewer's seat tech-lead

One day the chair turns around. You get senior enough that instead of sweating on the candidate's side of the table, you're the one running the hour — and most people discover, uncomfortably, that being good at interviews is no preparation for giving them well. A brilliant engineer can be a terrible interviewer: firing gotcha puzzles they happen to know, staring in silence while a nervous candidate spirals, then rendering a "gut feel" verdict that's really just "reminds me of me." Those bad habits don't just misjudge one person — they quietly poison your whole team's hiring bar.

Think of the difference like a driving examiner versus a street racer. The racer is fast, but you'd never want them scoring your test — they'd grade on flash, not on whether you can safely merge onto a motorway. A good interviewer is the calm examiner: they have a consistent rubric, they're measuring the specific competencies the job needs, and they run the same fair assessment for everyone so the results actually mean something. The goal isn't to find people who impress you; it's to predict who will do the job well.

The core skill this section teaches is designing for signal over trivia. Signal is evidence that predicts on-the-job performance: did the candidate clarify before assuming, reason about trade-offs, test their own work, communicate clearly, recover when stuck? Trivia is memorized facts and rare tricks that predict nothing except whether they read the same blog post you did. A strong interviewer picks realistic problems, offers hints to unblock a stuck candidate (then grades the recovery, which is itself signal), works to make the candidate comfortable enough to show their best thinking, and takes calibrated notes tied to concrete evidence rather than impressions. The table below contrasts the weak and strong versions of each move.

There's a mistake even well-meaning interviewers make: mistaking a comfortable, nervous-free candidate for a weak one, or letting an anxious performance mask real ability — which is why the deliberate work of putting someone at ease is part of the craft, not just kindness. And here's the payoff that makes this section matter even if you never plan to lead: understanding the rubric from the interviewer's side makes you a dramatically better candidate. Once you can see exactly what signal each of your moves sends — why clarifying scores, why narrating scores, why self-testing scores — the process stops feeling like a hazing ritual and becomes a game whose rules you can see and play on purpose.

candidate's moves clarify first narrate aloud weigh trade-offs self-test recover if stuck the rubric consistent · fair · same for everyone Signal predicts on-the-job work Trivia gotchas · predict nothing A good interviewer scores signal, not trivia. The same rubric runs for every candidate; moves that predict on-the-job behaviour count, memorized gotchas don't — and seeing this from the other seat tells you exactly which of your own moves earn points.
🗺️ How to read this diagram

Read left to right: your visible moves flow through the interviewer's rubric and land as one of two outcomes.

  • The left column of blue boxes is what you actually do in the room — clarify before assuming, narrate, weigh trade-offs, self-test, recover when stuck.
  • The middle box is the rubric a strong interviewer applies consistently to everyone, so results mean something rather than "reminds me of me".
  • The two arrows out of the rubric split into the payoff: Signal (green) — evidence that predicts how you'll perform on the job, which is what those left-column moves generate — versus Trivia (red) — memorized gotchas that predict nothing and that a good interviewer deliberately avoids testing.
  • The "aha": every move in the left column is designed to produce green signal, which is why understanding this pipeline from the interviewer's seat makes you a sharper candidate.

In short: Interviews score signal (behaviour that predicts the job), not trivia. Your clarify/narrate/test/recover moves are exactly the levers that generate it.

As a lead you'll give these interviews. Good interviewing is a skill: a consistent rubric, signal over trivia, making candidates comfortable enough to show their best, and calibrated notes. Knowing the rubric from this side also makes you a far better candidate.

Weak interviewerStrong interviewer
gotcha triviarealistic problems
stares silentlyhints to unblock, observes recovery
"gut feel" verdictrubric + concrete evidence
tests memorizationtests thinking + collaboration

Exercise CR3.1 — Run the full loop

Context: The five-step loop — clarify, approach + complexity, code, test, complexity — only becomes automatic by running it aloud on a real problem and catching where you go silent.

Your task: Take any DSA problem you've solved and do a mock interview out loud (record yourself): clarify, state approach + complexity, code, then write asserts covering normal + edge cases, and state final big-O. Note where you went silent — that's what to practice.

Requirements:

  • Clarify: restate the problem and ask about inputs, edge cases, constraints
  • Approach: propose out loud and state complexity before coding
  • Code: implement while narrating
  • Test: trace normal and edge cases with assertions
  • Complexity: state final time and space; record yourself and note the silent stretches

💡 Hint: The silence is the signal — the moments you stop narrating are exactly the habits to rehearse until they're automatic.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · State complexity before you codeBeginner

Context: Coding interviews grade the process, not just the answer. Stating the brute-force and the better approach with their complexities before coding signals you're choosing an approach on purpose.

Your task: You're asked: "Return the first value that appears twice in a list, or None." Before writing code, state the brute-force and the better approach with their complexities, then implement the better one.

Requirements:

  • Name brute force (nested loops, O(n²) time, O(1) space) out loud first
  • Name the better approach (a seen set, O(n) time, O(n) space)
  • Implement the set-based version
  • Return the first value already in seen, else None
  • Lead with complexity to show a deliberate choice, not a stumble

💡 Hint: Track what you've already seen in a set and return on the first repeat — the trade is spending O(n) space to buy O(n) time.

Show solution

Say this first: “Brute force is nested loops — O(n²) time, O(1) space. I can do O(n) time with O(n) space by remembering what I’ve seen in a set.” Then code the better one:

def first_repeat(nums):
    seen = set()
    for x in nums:
        if x in seen:
            return x
        seen.add(x)
    return None

print(first_repeat([3, 1, 4, 1, 5]))   # 1
print(first_repeat([1, 2, 3]))         # None

Leading with complexity signals you’re choosing an approach on purpose, not stumbling into one.

Exercise 2 · Think aloud through the two-pointer trickIntermediate

Context: On a sorted array, the two-pointer trick beats a hash map on space — and narrating why the sorted property enables it is what the interviewer scores, more than the code.

Your task: "Given a sorted array and a target, return indices of two numbers that sum to the target." Narrate the key realization (why sorted lets you avoid the hash map) and implement it in O(n)/O(1).

Requirements:

  • Explain aloud that sortedness lets you converge two pointers from the ends
  • Move the right pointer left when the sum is too big, the left pointer right when too small
  • Achieve O(n) time and O(1) space — no hash map
  • Return the pair of indices when the sum matches
  • Emphasise stating WHY the property enables the technique

💡 Hint: Let the sum tell you which pointer to move — too big means shrink from the right, too small means grow from the left.

Show solution

Narration: “Because it’s sorted, I can put one pointer at each end. If the sum is too big I move the right pointer left; too small, move left pointer right. That’s O(n) time and O(1) space — no hash map needed, which beats the unsorted version on space.”

def two_sum_sorted(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo < hi:
        s = nums[lo] + nums[hi]
        if s == target:
            return (lo, hi)
        if s < target:
            lo += 1
        else:
            hi -= 1
    return None

print(two_sum_sorted([1, 3, 4, 5, 7, 11], 9))   # (2, 3) -> 4 + 5

Saying why the property (sorted) enables the technique is the signal the interviewer scores, more than the code itself.

Exercise 3 · Write tests that expose your own bugsAdvanced

Context: Volunteering edge-case tests before being asked proves you think about correctness the way production does. After solving "merge two sorted lists", enumerate the cases that catch a subtle bug.

Your task: After solving "merge two sorted lists," enumerate the edge cases out loud and write assertions that would catch a subtle bug (e.g. an off-by-one or a dropped tail).

Requirements:

  • Name edge cases: empty inputs, one list exhausted first, duplicates, single elements, interleaved vs ordered
  • Assert both-empty and one-side-empty cases
  • Assert an interleaved case that also exercises the trailing tail
  • Assert a duplicates case
  • Call out the classic bug: forgetting to append the remaining tail

💡 Hint: The dropped-tail bug hides until one list outlasts the other — a test where the lists have different lengths is what surfaces it.

Show solution

Edge cases to name: empty inputs, one list exhausted first, duplicates, single elements, already-ordered vs interleaved.

def merge(a, b):
    i = j = 0
    out = []
    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            out.append(a[i]); i += 1
        else:
            out.append(b[j]); j += 1
    out.extend(a[i:])          # the dropped-tail bug lives here if you forget it
    out.extend(b[j:])
    return out

assert merge([], []) == []
assert merge([], [1, 2]) == [1, 2]          # one side empty
assert merge([1, 5], [2, 3, 9]) == [1, 2, 3, 5, 9]  # interleave + tail
assert merge([1, 1], [1]) == [1, 1, 1]      # duplicates
print("all tests passed")

Volunteering the tail and empty-input cases proves you think about correctness the way production does.

Exercise 4 · Recover out loud when you’re stuckExpert

Context: Getting unstuck methodically — state a correct baseline, name the wasted work, then optimize — often scores higher than reaching the perfect idea instantly.

Your task: You blanked on the optimal approach for "longest substring without repeating characters." Show the recovery script — how to talk and what to write — that turns being stuck into a passing signal, then land the sliding-window solution.

Requirements:

  • Say it aloud: start with something correct even if slow
  • Name the brute-force baseline and its cost
  • Identify the wasted work (re-scanning) as the thing to eliminate
  • Pivot to a window that remembers each character's last index
  • On a repeat, jump the window start past the previous occurrence

💡 Hint: Track the last index of each character and advance the window start past any repeat — the pivot is naming the re-scan as the waste to cut.

Show solution

Recovery script: “Let me start with something correct even if slow, then optimize.” Brute force: check every substring — O(n³). “The repeated work is re-scanning; I can keep a window and remember the last index of each char.” That’s the insight; now code it:

def longest_unique(s):
    last = {}
    start = best = 0
    for i, ch in enumerate(s):
        if ch in last and last[ch] >= start:
            start = last[ch] + 1     # jump the window past the repeat
        last[ch] = i
        best = max(best, i - start + 1)
    return best

print(longest_unique("abcabcbb"))   # 3  ("abc")
print(longest_unique("bbbbb"))      # 1
print(longest_unique(""))           # 0

Interviewers pass candidates who get unstuck methodically (state a correct baseline, name the wasted work, optimize) far more than ones who freeze waiting for the perfect idea.

Exercise 5 · Handle the take-home / practical variantProfessional

Context: A take-home is scored on engineering, not just the algorithm. Knowing what to add that a whiteboard skips — structure, a docstring, input validation, a test — is the difference between "works" and "would merge this".

Your task: A take-home asks you to "parse a log file and report the top-3 slowest endpoints." List what to add that a whiteboard answer skips, then give a clean solution.

Requirements:

  • Add readable structure, a docstring, input validation, and at least one test
  • Tolerate malformed lines (skip them) rather than crashing
  • Aggregate durations per endpoint (e.g. a defaultdict(int))
  • Select the top-k efficiently (e.g. heapq.nlargest)
  • Note complexity and assumptions; don't over-engineer

💡 Hint: The malformed-line guard, the test, and the docstring are exactly the parts a whiteboard lets you skip — and exactly what a take-home is grading.

Show solution

What take-homes score that whiteboards don’t: readable structure, a docstring, input validation, a test, and a note on complexity/assumptions. Add those — don’t over-engineer.

from collections import defaultdict
import heapq

def top_slow_endpoints(lines, k=3):
    # Given "METHOD /path DURATION_MS" lines, return the k endpoints with
    # the highest total duration as (path, total_ms), descending.
    totals = defaultdict(int)
    for ln in lines:
        parts = ln.split()
        if len(parts) != 3:
            continue                     # tolerate malformed lines
        _, path, dur = parts
        totals[path] += int(dur)
    return heapq.nlargest(k, totals.items(), key=lambda kv: kv[1])

sample = ["GET /a 120", "GET /b 300", "GET /a 200", "bad line", "GET /c 50"]
assert top_slow_endpoints(sample, 2) == [("/a", 320), ("/b", 300)]
print("passed")

The malformed-line guard, the test, and the docstring are the difference between “works” and “would merge this.” That’s what a take-home is actually measuring.

Exercise 6 · Grade a candidate from the interviewer’s seatIndustry scenario

Context: Two candidates can reach the same correct code and earn very different signals. Building the rubric yourself proves you understand interviews score the process, because it predicts on-the-job behaviour.

Your task: You're interviewing for "validate balanced parentheses." Two candidates both reach the correct stack solution. Write the rubric you'd use and decide who gets the higher signal — and why.

Requirements:

  • Rubric dimensions: clarifying questions, complexity stated, edge cases tested, communication
  • Candidate A clarifies, states complexity up front, tests edge cases unprompted, narrates the invariant
  • Candidate B assumes, codes silently, states complexity only when asked, tests only the happy path
  • Give A the higher signal despite identical code
  • Justify it: the process predicts behaviour on a harder problem

💡 Hint: Correct-but-silent reads as a coin flip on the next, harder question — the clarifying, narrating, self-testing candidate is the safer bet.

Show solution

Both produced the same working code:

def balanced(s):
    pairs = {")": "(", "]": "[", "}": "{"}
    stack = []
    for ch in s:
        if ch in "([{":
            stack.append(ch)
        elif ch in pairs:
            if not stack or stack.pop() != pairs[ch]:
                return False
    return not stack
📋 Interviewer scoring grid
SignalCandidate ACandidate B
Clarifying QsAsked: only these 3 bracket types? ignore other chars?Assumed, started coding
Complexity stated“O(n) time, O(n) space” up frontOnly when asked
Edge casesTested “”, “(]”, “(((” unpromptedTested happy path only
CommunicationNarrated the stack invariantCoded silently, correct

A gets the higher signal despite identical code. Interviews score the process — clarifying, stating complexity, self-testing, narrating — because that predicts on-the-job behavior. Correct-but-silent (B) reads as a coin flip on a harder problem.

✓ Checkpoint — you can move on when you can…

  • Run clarify→approach→code→test→complexity.
  • Think aloud throughout.
  • Self-test and state big-O.
  • Recover methodically; understand the interviewer's rubric.

Knowledge check check yourself

✓ Knowledge check

What are the five steps of the coding-interview process that interviewers grade, in order?

Show answer
Clarify (restate, ask about inputs/edge cases/constraints), Approach (propose out loud, state complexity, get a nod), Code (implement while narrating), Test (trace examples and edge cases), and Complexity (state time/space).
✓ Knowledge check

If the elegant solution isn't coming, why does a working brute force beat a broken "optimal" one?

Show answer
A correct, tested simple answer (even O(n²)) plus a clear path to optimize often scores higher than a half-finished clever solution — and methodical recovery when stuck is itself graded, while going silent fails.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in