A teammate wants to replace merge sort with in-place quicksort to save memory — how do you respond?
answer
- start with the requirement, not the algorithm
- who depends on the order of ties
- what does the buffer actually hold
- recursion depth is memory too
- an original-position field restores tie order
basics
~20 sStart with the requirement, not the algorithm: ask whether anything depends on the order of equal keys. Quicksort's partitioning reorders ties, so chained multi-key ordering silently breaks. And in place still costs O(log n) of recursion stack, not zero memory.
solid answer
~60 sFirst establish whether tie order is a requirement. If results are built by chained sorts — order by departure time, then by price — an unstable sort keeps the price column correct while scrambling everything inside each price, and no exception is thrown. That is a behaviour change, not an optimisation. Second, size the saving honestly: the merge buffer usually holds references, not whole records, so on fat records it is a small fraction of the data you are already holding; and quicksort in place is not free, since recursing into the smaller partition still costs `O(log n)` of stack, and a careless implementation can reach linear depth and blow the stack. Third, note what else changes: quicksort trades merge sort's `O(n log n)` worst case for `O(n^2)` under a naive pivot on organised input, so you need a randomised or median-of-three pivot plus a fallback. If the memory ceiling is genuinely binding and ties must hold, sort references or key-and-position pairs rather than records — that shrinks the buffer without giving up stability.
go deeper
Know that these two algorithms differ in more than memory: one preserves the order of equal keys and one does not, and that difference can change visible output.
Explain each property that changes in the swap — tie order, worst-case bound, recursion stack — and why an in-place claim is about small auxiliary space rather than none.
Demonstrate you drive the decision from requirements and measurements: establish whether tie order is depended on, size the buffer against the real working set, and name the pivot mitigation the swap would need.
Own how such changes are governed. Decide whether tie order is a contract worth pinning with tests, and weigh the maintenance cost of a clever in-place stable scheme against simply sorting references.
## Answer the requirement question before the algorithm question The proposal frames the choice as memory versus memory. It is not. Merge sort and in-place quicksort differ on three properties at once — stability, worst-case time and auxiliary space — and only one of them is on the table in the proposal. The senior move is to name all three before agreeing to anything. **Stability.** Ask: does anything downstream depend on the order of records whose sort keys are equal? The usual reason it does is chained sorting: results ordered by one key, then re-sorted by a second, with the earlier ordering surviving inside ties. Under an unstable sort the visible column is still monotone, so it looks right; only the ordering inside each group of equal keys changes. There is no exception, no failure, and no obvious symptom — often just a test that asserts a full expected ordering and starts failing as the data drifts. Treat that as a behaviour change requiring the same scrutiny as any other contract change, not as an implementation detail. **Worst case.** Merge sort is `O(n log n)` on every input. Quicksort with a naive first-or-last pivot is `O(n^2)` on organised input — sorted, reverse-sorted, or heavily duplicated data with a poor partitioning scheme — which is exactly the shape production data likes to take. Mitigations are well known: randomised pivots, median-of-three, three-way partitioning for duplicate-heavy keys, and a depth-triggered fallback to heapsort to cap the worst case. If the proposal does not include one of these, the change trades a guarantee for an average. **Space, measured rather than assumed.** "In place" means small auxiliary space, not none. Recursion depth is space: an implementation that recurses into the smaller partition and iterates on the larger holds `O(log n)` frames; one that always recurses left can reach `O(n)` depth on adversarial input, and stack exhaustion is a crash rather than a slowdown. On the other side, ask what the merge buffer actually holds. If you are sorting references to records — the common case for anything with a payload — the buffer is `n` references, while the records themselves are untouched. Against a working set that already holds those records, a buffer of references is frequently a small percentage, not a doubling. The proposal deserves a number, not an intuition. ## What you can actually do if the ceiling is real Suppose the memory ceiling is genuine — a fleet-wide limit, a batch job that must fit alongside other work — and tie order is genuinely required. There are several honest options, each with a stated price: 1. **Sort a smaller thing.** Sort an array of references, or of `(key, original position)` pairs, then permute the records once. This shrinks both the buffer and the data movement, and it is usually the largest win available. 2. **Force stability onto an unstable sort.** Add the original position as the final tie-break in the comparison, so no two records ever compare equal. This makes any algorithm behave stably. The price is an extra field per record — which itself costs `O(n)` and may cancel the saving — plus one extra comparison on every tie. It is a real technique, not a free lunch. 3. **Use an in-place stable merge.** Block-rotation merging reaches constant extra space while preserving tie order, but does substantially more data movement and is significantly harder to implement and maintain. Only justified when the ceiling is hard and the alternatives have been measured. 4. **Accept instability, explicitly.** If nothing depends on tie order, say so in writing and add a test that documents the new contract, so the next engineer does not accidentally reintroduce a dependency on it. ## The organisational half of the answer The reason this question is asked at senior level is that the failure mode is social, not technical. An algorithm swap that changes tie ordering passes review because reviewers check whether the output is sorted, and it is. The defence is to make the requirement explicit: if tie order matters, there should be a test asserting the full expected order of a tied group, named so it reads as a contract rather than as an incidental assertion. Then any future swap — a refactor, a dependency upgrade, someone else's optimisation — fails loudly instead of shipping a subtly different page. ## How to say it in an interview Don't answer "no" and don't answer "yes". Answer with three questions in order: *does anything depend on tie order; what does the buffer actually hold; and what is the worst-case bound we are giving up?* Then give the fallback that satisfies both constraints — sort references, or decorate with position — and finish with the test that pins the behaviour. That sequence shows you evaluate a change by its contract impact first and its resource impact second, which is exactly the judgment the question is probing.
- How would you make an unstable sort behave stably?Decorate each record with its original position and use that as the final tie-break, so no two records ever compare equal and the tie order is forced. The cost is an extra field per record — `O(n)` memory, which can cancel the reason you avoided the buffer — plus one extra comparison on every tie. It is a tradeoff, not a fix.
- Does calling quicksort in place mean it uses no extra memory?No. Recursion state is memory. Recursing into the smaller partition and looping on the larger bounds the depth at `O(log n)`; an implementation that always recurses into one fixed side can reach `O(n)` depth on adversarial input, and that shows up as a stack overflow rather than as a slow sort.
- If the memory ceiling is hard and ties must hold, what would you actually ship?Sort references or `(key, position)` pairs instead of whole records, so the buffer holds pointers rather than payloads, and permute once at the end. Keep the stable algorithm, document that tie order is part of the contract, and add a test asserting the order within a tied group so a future algorithm swap fails loudly.
- What would you measure before agreeing to the swap?Peak memory attributable to the buffer versus the rest of the working set, the record size and whether you are sorting payloads or references, the distribution of input orderings you actually see, and the sort's share of end-to-end latency. If the buffer is a few percent of the working set, the change is risk without reward.
saying these in an interview costs you the question
- Quicksort is in place so it uses no extra memory
- Stability is a nice-to-have, never a requirement
- Just swap it and see whether tests fail
- Quicksort is always faster, so always pick it
- The merge buffer always doubles memory usage