How would you sort 5 million order records by region then signup date in a memory-capped container?
answer
- clarify what the ordering must guarantee
- one composite key beats two passes
- count what the sort actually moves
- the buffer holds records or references
- a low-cardinality field invites bucketing
basics
~20 sSort once on a composite key of region then signup date, so no stability is required, and cut peak memory by sorting an array of references or extracted key-plus-index pairs instead of moving wide records through an O(n) merge buffer.
solid answer
~50 sStart by pinning the requirement: a comparator over the pair (region, signup date) totally orders the records, so stability only matters if records tying on *both* fields must keep their arrival order — and if they must, adding a sequence number to the key removes the requirement entirely. Then account for memory honestly. The classic merge sort buffer is O(n) *records*, and 5 million wide records is where a container's ceiling gets breached; sorting an array of references, or of extracted (packed key, index) pairs, shrinks both the buffer and the bytes moved to a few tens of megabytes. If the ceiling is still tight, an in-place O(n log n) sort avoids the buffer at the cost of stability and locality. Region is low cardinality, so a bucketing pass by region followed by independent per-bucket date sorts caps peak memory at one bucket and parallelises cleanly — watch for skew.
go deeper
Know that ordering by two fields is expressed as one comparator that checks the first field and falls back to the second, and that a sort needing an extra full-size buffer can be a problem when memory is tight.
Explain what a sort actually moves — records versus references versus extracted keys — and why a composite comparator removes the need for stability except among records equal on every key component.
Interviewers expect you to interrogate the constraint before choosing: record width, tie semantics, region distribution, and the real ceiling. Then defend one plan and name what you would measure to know it was right.
Own the tradeoff between engineering the sort and engineering the data flow: if records can arrive already grouped by region, or keys can be precomputed upstream, the sorting problem shrinks before anyone picks an algorithm.
## Turn the ask into requirements Three constraints are on the table: an ordering over two fields, five million records, and a memory ceiling. Each maps to a different decision, and the strongest answers separate them instead of jumping to an algorithm name. **The ordering.** Ordering by region, then by signup date within a region, is a single comparator over the pair. That is worth stating explicitly because of a widespread misconception: multi-field ordering does **not** require a stable sort. Stability preserves the input order of records that compare **equal**, and under a composite comparator two records compare equal only when they match on region *and* signup date. If those full ties must retain arrival order, you have two options: use a stable algorithm, or append a monotonically increasing sequence number as a third key component — after which every record is distinct, ties do not exist, and any algorithm produces the same answer. The tiebreaker route is usually better, because it makes the result reproducible regardless of which sort runs. The alternative technique — sort by signup date, then sort again by region and rely on stability to preserve the date order — *works*, but only with a genuinely stable sort, and it costs two full passes. Prefer the one-pass composite comparator unless the two orderings are computed at different times by different code. **The size.** Five million is comfortably an in-memory problem for records of modest width, and saying so is part of the answer: do not reach for the out-of-core family before checking. Five million records at 200 bytes each is roughly a gigabyte of payload; at 2 kilobytes each it is ten gigabytes and the conversation changes. Ask for record width before deciding. **The ceiling.** This is where the real engineering lives, and the key question is *what the sort moves*. ## Accounting for memory precisely A merge sort over an array of records needs an auxiliary buffer holding n records — that is a second copy of the payload. Two moves shrink it dramatically: 1. **Sort references, not records.** Build an array of n references (or indices) and sort that with a comparator that dereferences. The auxiliary buffer becomes n pointer-sized slots — for 5 million records, tens of megabytes rather than gigabytes — and no record payload is copied. The cost is locality: every comparison chases a reference into scattered memory, so cache misses rise. For small records this is a net loss; for wide records it is a large win. 2. **Extract a sort key.** Build an array of (packed key, index) pairs where the packed key encodes region and signup date in a single comparable value. Comparisons then touch contiguous, cache-resident data and never dereference at all; only the final permutation touches the records. This is the classic key-extraction trick and it usually beats reference sorting on both memory traffic and comparison cost. If the ceiling is tight enough that even the pointer buffer is unwelcome, an in-place O(n log n) sort — heapsort, or a depth-limited partitioning hybrid — uses O(1) or O(log n) auxiliary space. You give up stability and some locality; with a sequence-number tiebreaker in the key, losing stability costs you nothing. ## Exploiting the shape of the key Region is a low-cardinality categorical field: perhaps dozens of distinct values. That invites a **distribution pass**: count records per region in one scan, compute offsets, place each record into its region's segment, then sort each segment by signup date independently. Properties worth naming: - Peak working set is one segment, not the whole dataset — directly useful under a ceiling. - Segments sort independently, so they parallelise with no coordination. - Concatenating segments in region order yields the final order with no merge step. - The risk is **skew**: if one region holds 70% of orders, the largest segment dominates and you have gained little on peak memory. Check the distribution before committing. Signup dates are also structured — a timestamp is a bounded fixed-width integer — so a radix pass over date within a segment is available if comparison cost turns out to dominate. That is a measurement-driven optimisation, not a default. ## The answer, assembled "Composite comparator over (region, signup date), with a sequence number appended if arrival order among full ties matters. Sort an array of extracted (packed key, index) pairs rather than the records themselves — that keeps the auxiliary buffer at pointer scale and the comparisons cache-friendly. If the container ceiling is still tight, distribute by region first and sort each segment by date, which bounds the peak working set to the largest segment; I would check region skew first. I would stay with the standard library sort throughout and only revisit if profiling shows comparison or copy cost dominating." That answer names the requirement, the memory model, the data shape and the escape hatch — which is precisely the judgment being probed.
- When would stability actually be required in this scenario?Only when records tying on both region and signup date must retain arrival order and you cannot add a tiebreaker to the key, or when the two orderings are applied in separate passes — sort by date now, by region later — and the second pass must preserve the first. A single composite comparator plus a sequence number eliminates the requirement in almost every real version of this task.
- Why does sorting references or extracted keys help here, and when does it hurt?It shrinks both the bytes moved and any auxiliary buffer to pointer scale per element, which is decisive when records are wide and the memory ceiling is the binding constraint. It hurts when records are small: dereferencing on every comparison scatters memory access and costs more in cache misses than the copying it saved. Extracted packed keys avoid that by keeping comparisons contiguous.
- How would a distribution pass by region change the plan?Region has low cardinality, so one counting scan plus one placement scan groups records into per-region segments in linear time. Each segment then sorts by date independently: peak working set becomes the largest segment, segments parallelise without coordination, and concatenation in region order needs no merge. The failure mode is skew — one dominant region rebuilds the original problem inside a single segment.
saying these in an interview costs you the question
- Multi-field ordering always requires a stable sort
- Merge sort is always safe; memory is not my concern
- Five million records must be sorted out of core
- Sorting references is always faster than sorting records
- Adding a tiebreaker to the key is cheating