Sample report

Sample data for a fictional student, from a scripted interview. The report was written by the same pipeline that writes yours.

Coding interview

Software Engineering (Python), regular level, two problems, run against their examples, Oct 18, 2026

Hire

If you read nothing else

At the Regular bar in this interview.

  • Strength: Clarifying questions that changed the solution: touching intervals, input size, ties at the boundary, output order.

    Can there be ties at the boundary?Your words, 21:38
  • Strength: Strong complexity work: sorting lower bound, m log k vs log m, amortized swallowing cost, and the n/(c+1) bound you derived under pressure.

    Sorting is O(n log n) and the sweep is O(n), so O(n log n) overall ... Space is O(n) for the merged listYour words, 15:35
  • Gap: Trace edge cases before you run. Your merge overwrote the end instead of taking the max, so a containing interval lost its end and the third example failed.

    I set the end to 3. I overwrite the end and, uh, lose the 10.Your words, 12:02

One moment from your delivery

You hesitated

In Q1 you hesitated: you took 9.2 s to start (your usual 3.0 s); you sped up to 176 words per minute (your usual 143).

  • Time to start 9.2 s vs your usual 3.0 s
  • Words per minute 176 vs your usual 143
Sort by start, with a key on the first element.Your words, 9:22

Try: Start with a one-line headline ('My approach is X because Y') and think aloud from there instead of in silence.

Your next task, 20 min

Trace edge cases before you run

Write merge intervals from scratch, hand-trace the contained, touching and identical cases on paper first, then run and compare against your traces.

What happened

You worked through two problems: merging overlapping intervals, then the k most frequent elements. On both you moved from a brute-force idea to an efficient solution, and you handled the follow-ups on streaming updates and limited memory. Two bugs — an overwritten interval end and unsorted output — only surfaced when the provided examples failed. The main takeaway: your reasoning is strong, but your own test cases should catch those bugs before the examples do.

At the bar

You are at or above the Regular bar on problem solving, complexity and communication; the gap is that both bugs were caught by the given examples rather than by tests you wrote yourself.

You answered all 2 planned questions.

After that

More ways to practice, in order of impact.

  1. 1

    Check output order, not just the values: Re-solve top-k frequent with a heap. Before running, write down the exact output format the problem asks for, then check your printed line against it character by character, not just as a set. 15 minutes.

  2. 2

    Lead with what you can derive: Pick three problems whose named technique you don't know. For each, spend five minutes saying out loud what constraints force and what you'd try, before naming any algorithm. 15 minutes total.

Scorecard

Each skill from the rubric, scored 1 to 5, with the moments it was based on.

  • You reach the right approach and can justify it, but on both problems the key idea came after a nudge. Practise asking yourself 'what ordering or bound makes only neighbours matter?' before the interviewer has to ask it.

    Sorting by start is better: once they're sorted by start, the next interval can only overlap the last block I've mergedYour words, 5:47
    the frequencies are small integers, I can bucket by frequency: an array of n plus one listsYour words, 30:14

    What good looks like: Reaching the sorted sweep and the bucket-count solution without the interviewer's nudge, and stating the key invariant out loud before you write any code.

    To reach 5: finds an efficient approach independently and justifies it.

    Next session, before any hint arrives, spend sixty seconds naming the structure or invariant that removes the brute-force comparisons, and say it.

  • Both bugs were real and both were caught by your own runs, which is good, but each was a case you had already named out loud. Turn the cases you mention into code before you run: max(end) on merge, and sort the output when the spec shows ascending order.

    so when I merge I need the max of the two ends, not just the new endYour words, 12:02
    The set of values is right, it's just the order. I'll sort the outputYour words, 28:05

    What good looks like: Writing the merge with the max of the two ends from the start, and checking the required output order against the spec, so no provided example fails.

    To reach 5: clean, correct, idiomatic and handles edge cases.

    Before running, re-read the spec line by line and check each branch: contained intervals, touching intervals, and the exact output order.

  • Your analysis is precise and you spot trade-offs others miss (list shifting, empty buckets, Timsort memory). Read up on Misra-Gries / count-min sketch so the streaming case is a known answer rather than a guess.

    if the intervals are tiny and don't overlap, the output is just the input in sorted order, so merging is at least as hard as sortingYour words, 16:02
    Each block can be swallowed at most once, after that it's gone. So over m insertions the total swallowing work is at most mYour words, 18:25

    What good looks like: You are at the top anchor here: bounds, a lower-bound argument and trade-offs all landed. Keep stating the final complexity in one sentence before coding.

    To reach 5: precise analysis and identifies better alternatives and their trade-offs.

    Keep the habit; next time give the complexity of the solution you actually choose in one sentence before you start writing.

  • You test the given examples reliably; the gap is testing beyond them unprompted. Before coding, write down three edge cases (empty, contained, touching) and run them without being asked.

    The empty input, n is zero, so it should print nothing.Your words, 13:51
    Other cases I'd want: all intervals identical, they should collapse to one. One interval that contains everything, which is the case that just failed.Your words, 14:26

    What good looks like: Turning those named cases into real inputs and running them before the interviewer asks, especially the contained interval you had already described.

    To reach 5: proactively builds test cases, finds own bugs.

    Next session, before running the provided examples, write three of your own inputs — empty, contained, touching — and run them first.

  • This was the best part of the session: structured, narrated and full of the right questions. Keep doing exactly this.

    Are the intervals sorted when they come in? And if one ends at 3 and the next one starts at 3, do they count as overlapping?Your words, 3:19
    if merged is non-empty and the start is less than or equal to the last block's end, I extend it, otherwise I append a new pairYour words, 9:22

    What good looks like: Already at the top anchor: clarifying questions, a stated plan and a running commentary. Keep the same structure and add a one-line plan summary before coding.

    Keep narrating; open each problem with a two-sentence plan summary before you touch the editor.

Also went well

  • Clear invariant reasoning — you proved why only the last merged block can overlap, twice, and reused the argument for the streaming version.

Also to improve

  • Code the edge cases you have already named: max(end) on merge and sorting the output would have avoided both failing runs.
  • Test beyond the provided examples without being prompted — empty input, contained intervals, all-identical.
  • When a version passes, ask whether it passes for the right reason; the first top-k passed by luck of the example ordering.

Red flags

  • Twice a version passed the sample tests while being wrong against the spec, and you noticed only when a test failed.

Question by question

Open a question to see which key points you covered, in your own words, and what a stronger answer sounds like.

  1. Your answer: You asked clarifying questions, then started from an O(n^3) pairwise merge, improved to sorting by start plus a single sweep, and fixed the contained-interval bug with max(end). You tested empty input, explained O(n log n) time and O(n) space, and handled the streaming, cancellation and gap follow-ups.

    Key points

    • sort by start, then extend the last block (Covered)

      Sort by start, with a key on the first element ... if merged is non-empty and the start is less than or equal to the last block's end, I extend itYour words, 9:22
    • O(n log n) time, O(n) extra space (Covered)

      Sorting is O(n log n) and the sweep is O(n), so O(n log n) overall ... Space is O(n) for the merged listYour words, 15:35
    • edge cases: contained, identical, touching, empty (Covered)

      Other cases I'd want: all intervals identical, they should collapse to one. One interval that contains everything, which is the case that just failed.Your words, 14:26

    Follow-up questions

    • Describe the approach and its cost before coding. Handled wellYou gave the pairwise baseline and its O(n^3) worst case before improving it.
    • Walk through the failing contained-interval input. Handled wellYou found that you overwrote the end and changed it to the maximum of the two ends.
    • What else would you test beyond the four examples? Handled wellYou added empty input, identical intervals, a containing interval and a touching chain.
    • How would you keep the merged set updated as intervals arrive? Handled wellYou kept blocks sorted, used binary search, and noted list insertion is O(n).

    A stronger answer

    I'd first ask whether touching intervals count as overlapping and whether the input is sorted; here touching ones merge and the input is unsorted. My plan is to sort by start, then sweep once: keep a list of merged blocks, and for each interval compare its start with the last block's end. If it starts at or before that end, I extend the block, but I set the end to the maximum of the two ends so a contained interval does not shrink it. Otherwise I append a new block. On 1 3, 2 6, 8 10, 15 18 that gives 1 6, 8 10, 15 18. The sort is O(n log n) and the sweep is O(n), with O(n) space for the output. I'd test empty input, identical intervals, a containing interval and a touching chain.

    Common mistakes on this question

    • Overwriting the last block's end with the new interval's end, which breaks when the new interval is contained inside it.
    • Checking only the provided examples instead of tracing a contained case before running the code.
  2. Your answer: You counted with Counter, wrote a sorting version, then moved to a size-k min-heap and fixed output order by sorting the k results. You then used frequency buckets for O(n), and reasoned through fixed-memory heavy hitters and sliding-window top k.

    Key points

    • count with a hash map, then size-k heap (Covered)

      A min-heap of size k, keyed on frequency. I push every value with its count, and when the heap gets bigger than k I pop the smallestYour words, 24:59
    • sorting counts costs O(n log n) worst case (Covered)

      Counting is O(n), sorting the distinct values is O(m log m), so O(n plus m log m)Your words, 24:33
    • boundary cases: ties, k equals distinct, single element (Partly)

      Can there be ties at the boundary?Your words, 21:38

      You asked about ties, but you did not test k equal to the number of distinct values or a single-element array.

    • fixed-memory approximate heavy hitters (Partly)

      anything that appears more than n over c plus one times must still have a counter at the endYour words, 34:07

      You derived the heavy-hitters bound; a count-min sketch is the other standard answer, estimating counts with hashing in fixed memory.

    A stronger answer

    I'd clarify that the answer is unique, order does not matter, and negative values can appear. First I'd count with a dictionary or Counter in one pass. Then I'd keep a min-heap of at most k (frequency, value) pairs: push each distinct value, and whenever the heap has more than k items, pop the smallest frequency. That costs O(n + m log k), where m is the number of distinct values, which is much better than sorting when k is small. At the end I'd sort the k values so the output matches the examples, which adds only k log k. If I needed O(n), I'd bucket values by frequency from 1 to n and walk down from the highest bucket until I have k. For a huge stream with fixed memory, I'd discuss approximate heavy hitters with a fixed number of counters.

    Common mistakes on this question

    • Printing in heap order and forgetting that the examples expect ascending output order.
    • Assuming the first version is correct because the provided examples pass.

    Hints used: 1

Your code, each issue with the line it is about, and the complexity you said against the actual one.

1Given a list of intervals with start and end times, merge all overlapping intervals and return the result.

Your code, sent at 13:25

import sys  def merge(intervals):    intervals.sort(key=lambda iv: iv[0])    merged = []    for start, end in intervals:        if merged and start <= merged[-1][1]:            merged[-1][1] = max(merged[-1][1], end)        else:            merged.append([start, end])    return merged  def main():    data = sys.stdin.read().split()    if not data:        return    n = int(data[0])    nums = list(map(int, data[1:1 + 2 * n]))    intervals = [[nums[i], nums[i + 1]] for i in range(0, 2 * n, 2)]    for start, end in merge(intervals):        print(start, end)  main() 
  • Riskline 20The parsing slices exactly 2*n numbers without checking they exist, so a short or malformed input blows up two lines later with an index error instead of a clear message.

    nums = list(map(int, data[1:1 + 2 * n]))

    Fix: Check len(data) >= 1 + 2 * n (or that the leftover token count is even) right after reading n, and return or raise a clear error if it is not.

  • Styleline 5merge sorts the caller's list in place, so anyone who passes a list in gets it silently reordered.

    intervals.sort(key=lambda iv: iv[0])

    Fix: Use sorted(intervals, key=...) to leave the input untouched, or say in the docstring that the input is reordered.

Complexity: time O(n log n), space O(n) · matches what you said

Right: the sort dominates and the sweep is linear, with O(n) for the merged output on top of the sort's own scratch space.

Edge cases to check

  • A contained interval listed before its container, e.g. 2 3 then 1 10: The example that failed had the container first; after sorting the other order can also occur, and the max on the end is what keeps the 10.
  • Two intervals with the same start but different ends, e.g. 1 5 and 1 2: The sort key is only the start, so their relative order is arbitrary and the result must not depend on it.

A cleaner outline

  1. Read all tokens once, take n, build the pairs, and return early if there are no tokens at all.
  2. Sort the pairs by start.
  3. Sweep with a result list of [start, end]: compare each start to the last block's end with <=, then either extend using max of the two ends or append a new block.
  4. Print one block per line, joining the lines into a single write when n is large.

2Given an array of integers and an integer k, return the k most frequent elements.

Your code, sent at 31:22

import sysfrom collections import Counter  def top_k(nums, k):    counts = Counter(nums)    buckets = [[] for _ in range(len(nums) + 1)]    for value, freq in counts.items():        buckets[freq].append(value)    out = []    for freq in range(len(nums), 0, -1):        for value in buckets[freq]:            out.append(value)            if len(out) == k:                return out    return out  def main():    data = sys.stdin.read().split()    n = int(data[0])    nums = list(map(int, data[1:1 + n]))    k = int(data[1 + n])    print(*sorted(top_k(nums, k)))  main() 
  • Styleline 7The bucket array is sized by the length of the input even when there are only a few distinct values, so most of it is empty lists.

    buckets = [[] for _ in range(len(nums) + 1)]

    Fix: Size it by max(counts.values()) + 1, or keep the heap version, when the number of distinct values is much smaller than n.

Complexity: time O(n), space O(n) · matches what you said

Right: counting plus the bucket walk is linear, and the closing sorted() only adds O(k log k) over the k values you print.

Edge cases to check

  • A single element with k = 1: The smallest input, checking the bucket loop starts and stops at the right place.
  • k equal to the number of distinct values, e.g. three distinct values with k = 3: The walk must return every distinct value rather than stopping early or falling through the loop.

A cleaner outline

  1. Count values with a dictionary or Counter in one pass.
  2. Index a list of buckets by frequency, sized to the largest count rather than to n.
  3. Walk frequencies from high to low and collect values until you have k.
  4. Sort just those k values if the output has to be ascending.

Your code runs

26 runs, 22 of 25 example checks passed. Last run: passed.

Measured by the code runner, not judged.

  • 110 runs, 8 of 9 example checks passed
  • 216 runs, 14 of 16 example checks passed

Coaching

The few changes that matter most, with a way to practice each one.

  1. 1Code quality and correctness

    Trace edge cases before you run

    Your merge overwrote the end instead of taking the max, so a containing interval lost its end and the third example failed. You had described that exact contained case minutes earlier, so a quick hand-trace would have caught it before the run.

    I set the end to 3. I overwrite the end and, uh, lose the 10.Your words, 12:02

    How to practice: Write merge intervals from scratch. Before running anything, hand-trace three inputs on paper: one interval containing the others, a touching chain like 1 2, 2 3, 3 4, and all-identical intervals. Then run and compare. 20 minutes.

  2. 2Testing and edge cases

    Check output order, not just the values

    The heap version returned the right set of values in heap order, so two examples failed on ordering alone. The first version only passed because frequency order happened to match ascending order in those examples.

    That was, uh, luck, I never actually sorted by value.Your words, 28:05

    How to practice: Re-solve top-k frequent with a heap. Before running, write down the exact output format the problem asks for, then check your printed line against it character by character, not just as a set. 15 minutes.

  3. 3Communication

    Lead with what you can derive

    When the streaming question came up you opened with not knowing the standard approach, then went on to derive a workable fixed-counter scheme and its bound. That derivation is the valuable part, so put it first.

    Honestly, I don't know the standard answer here.Your words, 32:16

    How to practice: Pick three problems whose named technique you don't know. For each, spend five minutes saying out loud what constraints force and what you'd try, before naming any algorithm. 15 minutes total.

  • Honestly, I don't know the standard answer here.Your words, 32:16

    I don't know the standard name for this, but here's what I can reason: with fixed memory I can't keep exact counts, so I'd keep a fixed set of counters and accept approximate answers.

    You still admit the gap, but the interviewer hears your reasoning instead of a stop.

  • I, uh, wrote it as if the new interval always ends laterYour words, 12:02

    I overwrote the end instead of taking the max of the two ends, so the containing interval lost its 10. The fix is one line: keep the larger end.

    Naming the fix first makes the recovery read as control, not apology.

  1. Day 1Re-solve merge intervals; hand-trace three edge cases before running20 min
  2. Day 2List five edge cases for a new problem before writing any code10 min
  3. Day 3Write top-k with a heap and check output order against the spec20 min
  4. Day 4Rest day: skim your notes on the interval invariant5 min
  5. Day 5Say your reasoning out loud for three unfamiliar problems15 min
  6. Day 6On paper, re-derive the fixed-counter bound for streaming top-k15 min
  7. Day 7Full 25-minute mock: one interval problem, narrate, test first25 min

Pace, pauses, fillers and pitch, measured from your voice. Signals, not judgments.

Moments worth a look

  • You hesitated

    In Q1 you hesitated: you took 9.2 s to start (your usual 3.0 s); you sped up to 176 words per minute (your usual 143).

    • Time to start 9.2 s vs your usual 3.0 s
    • Words per minute 176 vs your usual 143
    Sort by start, with a key on the first element.Your words, 9:22

    Try: Start with a one-line headline ('My approach is X because Y') and think aloud from there instead of in silence.

  • You hesitated and used hedging phrases

    When asked “Okay. Say you can keep only a fixed number of counters, a...” (Q2), you hesitated and used hedging phrases: you took 7.0 s to start (your usual 3.0 s); 2 hedges such as 'maybe'.

    • Time to start 7.0 s vs your usual 3.0 s
    • Hedging 2 (“maybe”, “I'm not sure”)
    maybe decrement all of them, and drop the ones that hit zero?Your words, 33:07

    Try: Start with a one-line headline ('My approach is X because Y') and think aloud from there instead of in silence.

  • Your delivery got bumpy

    When asked “Before you change anything: what's happening on that input? Walk me through...” (Q1), your delivery got bumpy: 7 fillers per minute: 'um' (3), 'uh' (2), 'I mean' (1); 2 restarted sentences.

    • Fillers per minute 6.8 (“um”, “uh”, “I mean”)
    • Restarted sentences 2
    Um, let me trace it.Your words, 12:02

    Try: Swap fillers for a short silent pause; say this answer once more out loud and listen for them.

Voice signals

Measured in 46 of 51 answers with audio.

  • Your pitch went up at 29 of 317 measured phrase endings.

5 of 51 answers did not have enough clear audio to measure (too little speech, background noise).

You hedged 14 times; state the claim plainly first and add the caveat afterwards.

When you are unsure, name the property you need before asking for a nudge, so the question points somewhere.

Pace, pauses and fillers

Measured across 52 answers: average speaking rate 139 wpm, 1.2 fillers per speaking minute, average time to start answering 3.7 s, longest mid-answer pause 3.1 s.

Pace
139words/min
Fillers
1.2per min
Longest pause
3.1s
Time to first word
3.7s
Average before you started answering
Speaking time
83%
Share of answer time spent speaking
Hedges
14
Phrases like "I think" or "maybe"

Answer by answer

Pause Strong Hesitation Rushing Trailing off Rambling

The answers with a marked moment. Notes on single answers use typical speaking ranges.

Question 1

  • 0s61s
    Length
    61s
    Pace
    127
    To first word
    7.6s
    Fillers
    2
    Hedges
    1
    Pauses
    1
    • Hesitation (0s to 18s): Start came after 7.6 s, with 2 fillers and 1 hedge phrase in the answer.
  • 0s58s
    Length
    58s
    Pace
    122
    To first word
    4.1s
    Fillers
    6
    Hedges
    0
    Pauses
    0
    • Hesitation (0s to 14s): Start came after 4.1 s, with 6 fillers and 0 hedge phrases in the answer.
  • 0s39s
    Length
    39s
    Pace
    134
    To first word
    5.5s
    Fillers
    1
    Hedges
    2
    Pauses
    1
    • Hesitation (0s to 16s): Start came after 5.5 s, with 1 filler and 2 hedge phrases in the answer.

Question 2

  • 0s39s
    Length
    39s
    Pace
    127
    To first word
    6.6s
    Fillers
    0
    Hedges
    2
    Pauses
    1
    • Trailing off (30s to 39s): Voice level over the last 9 s was about 7 dB lower than earlier in the answer.
  • 0s51s
    Length
    51s
    Pace
    129
    To first word
    7.0s
    Fillers
    1
    Hedges
    2
    Pauses
    1
    • Hesitation (0s to 17s): Start came after 7.0 s, with 1 filler and 2 hedge phrases in the answer.
  • 27 steady stretches noted.
  • 4 hesitation episodes noted.
  • 1 trailing-off ending noted.

What Alex wrote down while listening.

  1. ClaimQuestion 13:19

    Asked about sorted input, touching intervals, size and output order before coding.

  2. ClaimQuestion 14:36

    First idea: compare every pair and restart; costed it as O(n^3) worst case when asked.

  3. ClaimQuestion 16:54

    Justified why only the last merged block can overlap.

  4. GapQuestion 111:02

    Overwrites the end on merge; the contained-interval example fails.

  5. ClaimQuestion 112:02

    Traced the failing example and fixed it with the max of the two ends.

  6. ClaimQuestion 113:51

    Tested the empty input and listed further edge cases when asked what else to test.

  7. ClaimQuestion 117:42

    Streaming follow-up: sorted structure plus bisect; noted list insertion is O(n).

  8. GapQuestion 227:43

    Youap version printed values in Youap order; two examples failed.

  9. ClaimQuestion 228:05

    Explained that the first version passed by luck; sorted the output.

  10. GapQuestion 232:16

    Did not know the standard streaming approach (sketches); said so.

  11. ClaimQuestion 233:07

    Reasoned toward a fixed-counter scheme with a nudge.

  12. ClaimQuestion 234:07

    Derived that values above n/(c+1) keep a counter; wanted to verify on an example.

Three more notes.

More next steps

  • Heaps and counting: You asked about ties, but you did not test k equal to the number of distinct values or a single-element array.
  • Heaps and counting: You derived the heavy-hitters bound; a count-min sketch is the other standard answer, estimating counts with hashing in fixed memory.
  • You hedged 14 times; state the claim plainly first and add the caveat afterwards.

102 turns, word for word.

  1. Alex0:02

    Hi, I'm Alex. Thanks for making the time. This is the coding round, about forty-five minutes, and we'll try to get through two problems: roughly twenty minutes each, then a few minutes at the end for your questions. There's an editor on the right, and you can run your code against the examples as often as you like. Any standard library is fine. If you get stuck, say what you're thinking; silence is harder for me to work with than a wrong idea. I'll mostly stay quiet while you code. Before we start, what are you working on these days?

  2. You0:39

    Hi, thanks. Um, so I'm in the last year of my computer science degree. Most of my time right now goes into my thesis project, which is a booking system for shared lab equipment, like microscopes and a couple of really expensive centrifuges. So, funnily enough, a lot of time slots and overlapping reservations. It's a Python backend with Postgres and a small web front end. And in the evenings I do practice problems, mostly to get used to talking while I code, which I'm, uh, still working on. I tend to go quiet when I'm concentrating, so feel free to poke me.

  3. Alex1:26

    Okay. Lab equipment, so people fight over the same slots. What's been the hardest part of that project so far?

  4. You1:34

    Honestly, two people booking the same slot at the same moment. The first version checked for a conflict and then inserted, and of course two requests could both pass the check. I ended up using an exclusion constraint in Postgres on the time range, so the database rejects the second one. It took me a while to even reproduce the bug, I had to write a little script that fires requests in parallel.

  5. Alex2:10

    How do you know the constraint actually fixed it, and not just made it rarer?

  6. You2:15

    Uh, fair question. I ran the same parallel script a few thousand times before and after. Before, it produced a double booking every few hundred runs, after, never, and the second request gets a clean error that the API turns into a conflict response. That's not a proof, but the database enforcing it is the part I trust, rather than my code checking first.

  7. Alex2:47

    Okay. Let's start. First problem. Given a list of intervals with start and end times, merge all overlapping intervals and return the result. Input is on standard input: the first number is n, then n pairs of start and end. Print the merged intervals one per line, start and end separated by a space. For example, 1 3, 2 6, 8 10 and 15 18 become 1 6, 8 10 and 15 18, because the first two overlap. The other examples are on the right.

  8. You3:19

    Okay. A couple of questions first. Are the intervals sorted when they come in? And if one ends at 3 and the next one starts at 3, do they count as overlapping? Because in my booking system they don't, a slot ending at three and one starting at three is fine, but here it might be different.

  9. Alex3:47

    Here they do. Touching intervals merge: 1 to 3 and 3 to 5 become 1 to 5. The input is not sorted, and the list may be empty, in which case you print nothing. A start is never bigger than its end, and both ends are integers.

  10. You4:05

    Okay, good to know. And how big can n get? Just so I know if something quadratic is even on the table. And the output, sorted by start, I assume, like in the examples?

  11. Alex4:22

    Assume up to a hundred thousand intervals, and values that fit in a normal integer. Output sorted by start, yes. Before you write anything, I'd like to hear the approach and roughly what it costs.

  12. You4:36

    Okay. So, um, my first thought is the naive one, just to have something. Compare every pair, and whenever two overlap, merge them into one, remove the two originals, and start over from the beginning, until a full pass finds nothing to merge. That definitely works, it's correct by construction because it only stops when nothing overlaps, it's just slow.

  13. Alex5:09

    How slow, roughly?

  14. You5:10

    One pass over all pairs is n squared. And every merge can trigger another full pass, and there can be up to n minus one merges, so worst case it's, um, n cubed. With a hundred thousand intervals that's ten to the fifteen, so, no. Even n squared is ten to the ten, which in Python is way too slow.

  15. Alex5:42

    Okay. Can you do better? Think about what forces you to compare every pair.

  16. You5:47

    Hmm. The expensive part is that an interval could overlap anything anywhere in the list. If I put them in some order first, then maybe only neighbours matter. Sort by end time? Then, um, no, wait. If I sort by end, an interval with a very early start could still come late and swallow several earlier ones, like 1 to 10 would come after 2 to 3 and 5 to 6. Sorting by start is better: once they're sorted by start, the next interval can only overlap the last block I've merged, because everything before that block already ended earlier, or got absorbed into it.

  17. Alex6:49

    Why can't the next interval overlap something before the last block? Convince me.

  18. You6:54

    Because the merged list is disjoint and sorted. Say the new interval reached back into an earlier block. Its start is at least the start of the last block, since we're going in order of start. So if it overlapped an earlier block it would have to start before that block ends, which is before the last block starts. Contradiction. So checking only the last one is enough. Then it's one pass: extend the last block, or start a new one. Sorting is n log n, the pass is linear.

  19. Alex7:39

    Okay. Walk me through the first example with that rule, before you code.

  20. You7:44

    Sorted by start it's 1 3, 2 6, 8 10, 15 18, already in order. Start the merged list with 1 3. Then 2 6: 2 is less than or equal to 3, so they overlap, the block becomes 1 6. Then 8 10: 8 is bigger than 6, so a new block. 15 18, bigger than 10, new block. So 1 6, 8 10, 15 18, which matches the expected output.

  21. Alex8:18

    Okay. Go ahead and code it. Talk me through it as you go; I'll mostly stay quiet until you run it.

  22. You8:26

    Sure. I'll read the input in main and keep merge as its own function, so I can test it separately. I'll read all of standard input at once and split, so I don't care how the pairs are laid out on the lines.

  23. You9:22

    So, merge. Sort by start, with a key on the first element. Then the merged list. For each start and end: if merged is non-empty and the start is less than or equal to the last block's end, I extend it, otherwise I append a new pair. Less than or equal, not strictly less, because touching intervals merge. And I store lists, not tuples, so I can update the end in place. Then main: n, then two n numbers, pair them up, print each block.

  24. You11:02

    Sent to the workspace, python

    import sys  def merge(intervals):    intervals.sort(key=lambda iv: iv[0])    merged = []    for start, end in intervals:        if merged and start <= merged[-1][1]:            merged[-1][1] = end        else:            merged.append([start, end])    return merged  def main():    data = sys.stdin.read().split()    if not data:        return    n = int(data[0])    nums = list(map(int, data[1:1 + 2 * n]))    intervals = [[nums[i], nums[i + 1]] for i in range(0, 2 * n, 2)]    for start, end in merge(intervals):        print(start, end)  main() 
  25. You11:03

    Okay, I think that's it. Let me run the first example.

  26. You11:17

    The first example passes. Let me run all of them.

  27. You11:41

    Three pass, the third one fails. That's the unsorted one where one interval contains the others. I print 1 3 and then 5 6, and it expects just 1 10. Hmm.

  28. Alex11:57

    Before you change anything: what's happening on that input? Walk me through it.

  29. You12:02

    Um, let me trace it. After sorting it's 1 10, then 2 3, then 5 6. Merged starts as 1 10. Then 2 3 starts inside 1 10, so I merge, but I set- I set the end to 3. I overwrite the end and, uh, lose the 10. Then, um, 5 6 doesn't overlap 1 3 anymore, so it becomes its own block. Um, so when I merge I need the max of the two ends, not just the new end. I, uh, wrote it as if the new interval always ends later, which is, I mean, exactly the contained case I mentioned five minutes ago.

  30. You13:25

    Sent to the workspace, python

    import sys  def merge(intervals):    intervals.sort(key=lambda iv: iv[0])    merged = []    for start, end in intervals:        if merged and start <= merged[-1][1]:            merged[-1][1] = max(merged[-1][1], end)        else:            merged.append([start, end])    return merged  def main():    data = sys.stdin.read().split()    if not data:        return    n = int(data[0])    nums = list(map(int, data[1:1 + 2 * n]))    intervals = [[nums[i], nums[i + 1]] for i in range(0, 2 * n, 2)]    for start, end in merge(intervals):        print(start, end)  main() 
  31. You13:33

    Now all four pass, including the touching one and the single point, seven seven.

  32. Alex13:46

    Mm-hm. The four examples are the ones I gave you. What else would you test?

  33. You13:51

    The empty input, n is zero, so it should print nothing. I also want to check that n zero doesn't crash in the parsing, since I slice the data. And completely empty standard input, which I handle with the early return. Let me run the zero one.

  34. You14:26

    Yes, it prints nothing and exits cleanly. Other cases I'd want: all intervals identical, they should collapse to one. One interval that contains everything, which is the case that just failed. A chain where each one touches the next, like 1 2, 2 3, 3 4, which should become 1 4. And negative times would work too, since I only compare numbers.

  35. Alex14:57

    Think about the upper end of the input size. Anything in the parsing or the output that hurts at a hundred thousand intervals?

  36. You15:06

    Not really. Reading everything at once is two hundred thousand numbers, a few megabytes at most. The one thing I'd change for really big inputs is printing: a print per line is slow in Python, so I'd join the lines and write once. At a hundred thousand it's fine.

  37. Alex15:31

    Okay. Time and space complexity of the final version?

  38. You15:35

    Sorting is O(n log n) and the sweep is O(n), so O(n log n) overall, the sort dominates. Space is O(n) for the merged list in the worst case, when nothing overlaps. And Python's sort, which is Timsort, can use up to n over two extra on top of that.

  39. Alex15:59

    Is n log n the best possible here?

  40. You16:02

    Hmm. In general, I think so, but I'm not sure how to prove it. Intuitively, if the intervals are tiny and don't overlap, the output is just the input in sorted order, so merging is at least as hard as sorting, and comparison sorting is n log n. If the coordinates were small integers you could do better with counting, but I wouldn't bet on that being allowed.

  41. Alex16:43

    Here's a follow-up, closer to your booking system. What if intervals arrive one at a time and you must keep the merged set up to date? Say a few hundred per second, and someone can ask for the merged set at any time.

  42. You16:59

    Um, so I'd keep the merged blocks sorted by start, all the time. For a new interval I binary search where its start goes, with bisect, and then I have to merge it with the blocks it overlaps, which is the one just before it, maybe, and then however many after it, I walk right while the next block starts before my end, and the—

  43. Alex17:37

    Sorry, before you go on. Why only the one just before it?

  44. You17:42

    Because the blocks before that one end before the previous block starts, they're disjoint. Same argument as before. So at most one block to the left can be touched. To the right, it can swallow several. Then the cost: finding the spot is log n, but inserting into or deleting from a Python list is O(n), because it shifts elements. A balanced tree or a sorted container would make it log n per block touched.

  45. Alex18:20

    One insertion can swallow many blocks. What's the cost amortized over many insertions?

  46. You18:25

    Hmm. Each block can be swallowed at most once, after that it's gone. So over m insertions the total swallowing work is at most m. So amortized it's O(log n) per insert with the tree. With the plain list it's still the shifting that hurts, that stays linear per insert.

  47. Alex18:54

    Python has no balanced tree built in. What would you actually use?

  48. You18:59

    There's the sortedcontainers package, which is a third-party library, but it's very widely used. If I couldn't add a dependency, honestly, I'd keep the plain list. At a hundred thousand blocks the shift is a memmove of under a megabyte, that's microseconds, and a few hundred inserts a second is nothing. I'd measure before writing a tree by hand.

  49. Alex19:29

    Now suppose bookings can also be cancelled, so an interval can be removed again after it was merged. What changes?

  50. You19:37

    Oh, that's much harder. Once two intervals are merged into one block I've lost the information about where each of them was, so I can't undo it. I'd have to keep the original intervals, not just the blocks. Um, then a removal could split a block into several pieces, and to find them I'd basically re-merge the originals that were inside that block. That's linear in the size of the block. For something better I think you'd want an interval tree or a segment tree with counts over the coordinates, but I haven't implemented one of those in a long time.

  51. Alex20:33

    Last one on this problem. How would you find the free gaps between merged intervals?

  52. You20:39

    That's easy once they're merged: walk the merged list and every pair of neighbours gives a gap from the previous end to the next start. If the ends are inclusive, uh, then for integers the gap is previous end plus one to next start minus one, and if they're touching there's no gap at all. Linear after the merge. That's basically the free-slot view in my booking system.

  53. Alex21:11

    Okay. Let's leave that one there. We're a bit over twenty minutes in, so let's move on.

  54. Alex21:19

    Second problem. Given an array of integers and an integer k, return the k most frequent elements. Input: n, then the n numbers, then k. Print the k values on one line. For example, 1 1 1 2 2 3 with k equal to 2 gives 1 and 2.

  55. You21:38

    Can there be ties at the boundary? Like if the k-th and the k-plus-first value have the same count, which one do I return?

  56. Alex21:52

    The answer is guaranteed to be unique, so you won't get a tie at the boundary. k is between 1 and the number of distinct values, so it's always possible to return k values.

  57. You22:05

    Okay. And the output order? And can the numbers be negative?

  58. Alex22:12

    Order doesn't matter for the answer, but the examples print them ascending. Yes, negatives are possible.

  59. You22:18

    Okay. So the first step is clearly counting: a dictionary from value to frequency, one pass, O(n). Negatives don't matter for a dictionary. Then the simple version is to sort the distinct values by count, descending, and take the first k. If m is the number of distinct values, that's O(n plus m log m). I'll write that first, so I have something that works, and then see if I can do better.

  60. Alex22:54

    Okay. Go ahead and write the simple version.

  61. You23:16

    Same reading as before. Counter from collections for the counting. Then sorted over the keys, with the count as the key, reversed, and slice the first k.

  62. You24:09

    Sent to the workspace, python

    import sysfrom collections import Counter  def top_k(nums, k):    counts = Counter(nums)    ordered = sorted(counts, key=lambda v: counts[v], reverse=True)    return ordered[:k]  def main():    data = sys.stdin.read().split()    n = int(data[0])    nums = list(map(int, data[1:1 + n]))    k = int(data[1 + n])    print(*top_k(nums, k))  main() 
  63. You24:22

    All four pass, including the negative one.

  64. Alex24:28

    What's the complexity of that, with n numbers and m distinct values?

  65. You24:33

    Counting is O(n), sorting the distinct values is O(m log m), so O(n plus m log m), and m can be as big as n, so O(n log n) worst case. Space O(m) for the counts.

  66. Alex24:52

    Think about what you're sorting. You only need the top k, not a full ordering. What structure exploits that?

  67. You24:59

    A heap. A min-heap of size k, keyed on frequency. I push every value with its count, and when the heap gets bigger than k I pop the smallest, so what's left at the end is the k biggest. Each push and pop is log of the heap size, so it's O(n plus m log n)... no, sorry, m log k, the heap never has more than k plus one items.

  68. Alex25:38

    When does the difference between log m and log k actually matter?

  69. You25:43

    When k is much smaller than m. Like the top ten out of a million distinct values: log ten instead of log a million, and the heap is tiny in memory. If k is close to m it's basically the same as sorting. Python also has heapq dot nlargest, which does exactly this, but I'll write it out.

  70. Alex26:11

    Walk me through the heap on the first example before you write it.

  71. You26:17

    Counts are 1 three times, 2 twice, 3 once, k is 2. Push three-comma-one, heap has one item. Push two-comma-two, two items, fine. Push one-comma-three, three items, more than k, so pop the smallest, which is the count one, value 3. Left with value 2 and value 1. Which is the answer.

  72. You27:26

    Sent to the workspace, python

    import heapqimport sysfrom collections import Counter  def top_k(nums, k):    counts = Counter(nums)    heap = []    for value, freq in counts.items():        heapq.heappush(heap, (freq, value))        if len(heap) > k:            heapq.heappop(heap)    return [value for freq, value in heap]  def main():    data = sys.stdin.read().split()    n = int(data[0])    nums = list(map(int, data[1:1 + n]))    k = int(data[1 + n])    print(*top_k(nums, k))  main() 
  73. You27:43

    Huh. Two fail now, the first one and the last one. Example 1 prints 2 1 instead of 1 2. And the last one prints 9 8 instead of 8 9.

  74. Alex27:59

    The set of values looks the same. And the first version passed all four. Why?

  75. You28:05

    Because, um, the first version printed in order of frequency, highest first, and in these examples that happens to be ascending by value too. That was, uh, luck, I never actually sorted by value. The heap gives them back in heap order, smallest frequency first. The set of values is right, it's just the order. I'll sort the output, that's k log k, which doesn't change the bound. I should have sorted in the first version too.

  76. You29:10

    Sent to the workspace, python

    import heapqimport sysfrom collections import Counter  def top_k(nums, k):    counts = Counter(nums)    heap = []    for value, freq in counts.items():        heapq.heappush(heap, (freq, value))        if len(heap) > k:            heapq.heappop(heap)    return [value for freq, value in heap]  def main():    data = sys.stdin.read().split()    n = int(data[0])    nums = list(map(int, data[1:1 + n]))    k = int(data[1 + n])    print(*sorted(top_k(nums, k)))  main() 
  77. You29:17

    All four pass again.

  78. Alex29:25

    Okay. Can you get it linear in n, without the log factor?

  79. You29:30

    Linear... Quickselect on the counts would be linear on average: partition the distinct values around the k-th largest frequency, like the partition step in quicksort, and recurse into one side only. But worst case it's quadratic, unless I do the median-of-medians thing, and it's fiddly to write in an interview. Um, is there something simpler...

  80. Alex30:09

    Think about the values you're ordering by. What's the largest a frequency can be?

  81. You30:14

    At most n. Oh, so the frequencies are small integers, I can bucket by frequency: an array of n plus one lists, put each value in the bucket of its count, then walk from the top bucket down until I have k values. That's O(n) time and O(n) space, no comparisons at all. Let me write that.

  82. You31:22

    Sent to the workspace, python

    import sysfrom collections import Counter  def top_k(nums, k):    counts = Counter(nums)    buckets = [[] for _ in range(len(nums) + 1)]    for value, freq in counts.items():        buckets[freq].append(value)    out = []    for freq in range(len(nums), 0, -1):        for value in buckets[freq]:            out.append(value)            if len(out) == k:                return out    return out  def main():    data = sys.stdin.read().split()    n = int(data[0])    nums = list(map(int, data[1:1 + n]))    k = int(data[1 + n])    print(*sorted(top_k(nums, k)))  main() 
  83. You31:33

    All pass. The final sort is only over k values, so it's still linear apart from that. In practice I'd probably still pick the heap, though. The buckets array is size n even when there are only a few distinct values, so it's mostly empty lists, and the heap version is easier to read.

  84. Alex32:01

    Different setting now. Say the numbers are product IDs from a click stream, billions a day, and you have one machine with a fixed amount of memory. What if the data is a huge stream and memory is limited?

  85. You32:16

    So I can't keep a count for every value. Honestly, I don't know the standard answer here. I think there's a probabilistic structure, a sketch of some kind, that keeps approximate counts in fixed memory, with hashing, but I couldn't explain how it works or what error it gives. What I can reason about is that you'd have to give up exact answers.

  86. Alex32:56

    Okay. Say you can keep only a fixed number of counters, a few thousand. A value with no counter arrives and every counter is taken. What do you do?

  87. You33:07

    Then, um, when a value comes that has a counter, increment it. If it doesn't and there's a free counter, take it. If they're all full... maybe decrement all of them, and drop the ones that hit zero? Then something really frequent can't be pushed out for long, because it gets incremented more often than everything gets decremented. So the heavy values survive. But the counts are only lower bounds, and I'm not sure about the exact guarantee, like how frequent something has to be to be safe.

  88. Alex33:59

    Mm-hm. You said frequent values survive. Try to work out how frequent a value must be to be safe.

  89. You34:07

    Okay. Say c counters and n items in the stream. Every time I decrement, I decrement c counters plus the new item I'm throwing away, so c plus one units of count disappear together. That can happen at most n over c plus one times in total. So a value's counter is at most n over c plus one below its true count. So anything that appears more than n over c plus one times must still have a counter at the end. I think that's right, but I'd want to check it on a small example.

  90. Alex35:06

    Okay. One more variation. What if frequencies change over time and you need the current top k? Say the top products in the last hour, refreshed every few seconds.

  91. You35:17

    Then I'd count over a window, like the last hour, with a count per value per minute, and drop the oldest minute as time moves. The totals change by small steps. Recomputing the heap on every query is m log k, so if queries are frequent I'd keep a sorted structure keyed on count that I update whenever a count changes, and read the top k off the end.

  92. Alex35:53

    Before we finish, back to the first problem. You said the sort can use extra memory. Would you change the merge if memory were tight?

  93. You36:03

    I could write the merged blocks back into the same list with a write index, and truncate at the end. The sort is already in place, intervals dot sort. Then it's O(1) extra apart from what the sort itself needs. It does modify the caller's list, though, so I'd say so in the docstring or name it something like merge in place.

  94. Alex36:36

    Okay. That's both problems. We have a few minutes left. Do you have any questions for me?

  95. You36:43

    Yes, um. What does a good first few months look like for someone joining as a new grad? Like, what would you want to see from them?

  96. Alex36:57

    It depends on the team. Usually a small, well-scoped project first, so you learn the codebase and the deploy process end to end. Asking questions early matters more than shipping fast, and there's a lot of code review in both directions. The hiring team can give you the specifics for this role, including who you'd work with and what the first project would likely be.

  97. You37:22

    Okay. And is this kind of problem close to the day-to-day work, or more of a filter?

  98. Alex37:32

    Some of both. Nobody merges intervals all day, but reading a problem carefully, testing your own code and reasoning about cost come up constantly. The reasoning and the testing matter more than the exact problem.

  99. You37:45

    Okay. And what happens after this round? How soon would I hear back?

  100. Alex37:53

    The report from this round is ready in a few minutes. The hiring team handles the next steps and the timing; they'll be in touch about that.

  101. You38:03

    Okay. That's all from me, thank you.

  102. Alex38:10

    Thanks, that's time. The report covers both problems and will be ready in a few minutes.

Mockich is a practice tool. Scores and delivery signals are guidance for your preparation, not a prediction of a real hiring decision.

Start another interview
Code runs in this session26 runs, 22 of 25 example checks passed

What the code runner returned to the student, in order: real executions of their code in the sandbox, on each question’s own data.

  1. 11:14Question 1Ran one example: Example 1: n, then n start/end pairs -> merged pairsPassed

    Python, the code sent at 11:02

    1 6
    8 10
    15 18
    

    Example 1: n, then n start/end pairs -> merged pairs: matches the expected output.

  2. 11:28Question 1Ran every example3/4 passed

    Python, the code sent at 11:02

    • 1 6
      8 10
      15 18
      

      Example 1: n, then n start/end pairs -> merged pairs: matches the expected output.

    • 1 5
      

      Edge: touching intervals: matches the expected output.

    • 1 3
      5 6
      

      Edge: unsorted, one contains the others: differs from the expected output.

      line 1, expected1 10yours1 3
    • 7 7
      

      Edge: a single point interval: matches the expected output.

  3. 13:30Question 1Ran every example4/4 passed

    Python, the code sent at 13:25

    • 1 6
      8 10
      15 18
      

      Example 1: n, then n start/end pairs -> merged pairs: matches the expected output.

    • 1 5
      

      Edge: touching intervals: matches the expected output.

    • 1 10
      

      Edge: unsorted, one contains the others: matches the expected output.

    • 7 7
      

      Edge: a single point interval: matches the expected output.

  4. 14:23Question 1Ran with their own input ("0")Ran

    Python, the code sent at 13:25

    The program printed nothing.

  5. 24:16Question 2Ran every example4/4 passed

    Python, the code sent at 24:09

    • 1 2
      

      Example 1: n, numbers, k -> the k values, ascending: matches the expected output.

    • 1
      

      Edge: a single element: matches the expected output.

    • 4
      

      Edge: negative numbers, k = 1: matches the expected output.

    • 8 9
      

      Edge: k equals the number of distinct values: matches the expected output.

  6. 27:32Question 2Ran every example2/4 passed

    Python, the code sent at 27:26

    • 2 1
      

      Example 1: n, numbers, k -> the k values, ascending: differs from the expected output.

      line 1, expected1 2yours2 1
    • 1
      

      Edge: a single element: matches the expected output.

    • 4
      

      Edge: negative numbers, k = 1: matches the expected output.

    • 9 8
      

      Edge: k equals the number of distinct values: differs from the expected output.

      line 1, expected8 9yours9 8
  7. 29:15Question 2Ran every example4/4 passed

    Python, the code sent at 29:10

    • 1 2
      

      Example 1: n, numbers, k -> the k values, ascending: matches the expected output.

    • 1
      

      Edge: a single element: matches the expected output.

    • 4
      

      Edge: negative numbers, k = 1: matches the expected output.

    • 8 9
      

      Edge: k equals the number of distinct values: matches the expected output.

  8. 31:27Question 2Ran every example4/4 passed

    Python, the code sent at 31:22

    • 1 2
      

      Example 1: n, numbers, k -> the k values, ascending: matches the expected output.

    • 1
      

      Edge: a single element: matches the expected output.

    • 4
      

      Edge: negative numbers, k = 1: matches the expected output.

    • 8 9
      

      Edge: k equals the number of distinct values: matches the expected output.

Sample report · Mockich