Does mutating the caller's buffer make an algorithm in-place, or is more required?
answer
- mutation and allocation are different questions
- how big is the scratch you keep?
- constant, not zero, extra memory
- a full-size temporary disqualifies it
- O(log n) recursion is usually tolerated
basics
~20 sMore is required. In-place means O(1) auxiliary space — a constant amount of scratch memory, not zero. A routine that allocates a full-size temporary and copies it back over the caller's buffer is destructive, not in-place.
solid answer
~50 sIn-place is a claim about *allocation*, not about *mutation*. The definition is O(1) auxiliary space: a constant number of indices, accumulators and swap temporaries, independent of input size. Some authors relax it to O(log n) to accommodate the recursion stack of a divide-and-conquer routine, and you should say which reading you mean. So a routine that builds an n-element scratch array, fills it, then copies it back over the caller's buffer is *destructive* but not in-place — it hit peak O(n) auxiliary along the way, which is exactly the cost an in-place claim promises to avoid. The two properties are also independent: an algorithm can be in-place without mutating anything the caller sees, and mutating without being in-place. When you claim in-place, name the constant-size scratch you do use, and separately warn the caller that their data will be overwritten.
code
pseudocode · 10 lines# v is the caller's buffer of measurements, length n, sum non-zero
s = 0
for i in 0..length(v)-1:
s = s + v[i]
scaled = new array of length(v)
for i in 0..length(v)-1:
scaled[i] = v[i] / s
for i in 0..length(v)-1:
v[i] = scaled[i]
return vgo deeper
Remember that in-place is a bound on extra memory — a constant amount, not none. Being able to say "a couple of indices and one temporary for the swap" is the answer expected at this level.
Explain the mutation-versus-allocation distinction and classify a routine correctly when it overwrites the input but built a full-size temporary to do it. Peak usage, not the final state, is what you are measuring.
Show you price the contract, not just the bound: who loses their original data, whether callers will copy defensively anyway, and what aliasing does to a destructive routine on a shared buffer.
Own the API-level call. Decide whether the library ships a destructive core with a copying wrapper, how destructiveness is signalled by naming, and when a fleet-wide memory ceiling makes the in-place variant non-negotiable.
## The definition, stated precisely An algorithm is **in-place** when its auxiliary space is O(1): a fixed number of scalars — loop indices, a swap temporary, a running accumulator — whose count does not grow with the input. The widely accepted relaxation is O(log n), allowing the call stack of a divide-and-conquer routine that recurses to logarithmic depth. Anything beyond that (a scratch array proportional to n, a map keyed by the elements, a queue holding a level of a structure) disqualifies the claim. Two things the definition does **not** say: - It does not say *zero* memory. Every algorithm needs somewhere to hold a comparison result and an index. "In-place means it uses no extra memory" is the single most common wrong answer here, and the follow-up that exposes it is simply "where do you put the element while you swap two of them?" - It does not say *mutates the input*. That is a separate property — destructiveness — and it is a property of the API contract, not of the memory bound. ## Mutation and allocation are independent axes | | Allocates O(1) | Allocates O(n) | | --- | --- | --- | | **Leaves input intact** | in-place, non-destructive: scanning for a maximum, or a two-index walk that only reads | not in-place: building a transformed copy | | **Overwrites input** | in-place, destructive: swap-based reordering of the caller's buffer | not in-place, destructive: fill a scratch array, copy it back | The bottom-right cell is the one candidates misclassify. Consider a routine that rescales a caller's measurement buffer so the values sum to one: sum the buffer, allocate a same-size scratch array, write each scaled value into it, then copy the scratch back over the caller's buffer. The caller observes their data changed, so it *feels* in-place — but peak auxiliary usage was n cells. On a buffer that occupies most of available memory, that hidden doubling is precisely the failure the in-place question is asking about. The fix in that example is trivial (write the scaled value straight back into the buffer during the second pass, since each output depends only on its own input cell and the already-computed sum), and noticing that the scratch was never needed is the point of the exercise. ## Why interviewers ask "can you do it in O(1) space?" Because it forces a different solution shape. Once you cannot allocate a table keyed by the data, you must either exploit structure already present (ordering, a bounded value range, spare capacity in the existing cells) or accept more time. That is a real tradeoff, not a puzzle: the constant-space version is frequently slower, less readable, or destroys information the caller may still need. A good answer states the price rather than presenting the constant-space version as strictly better. ## The API designer's chair If you are the one shipping the routine, in-place is a contract decision, and it has consequences beyond the memory number: - **Destructiveness must be visible in the name and the documentation.** A caller who wanted to keep the original and did not read carefully will lose it, and the bug surfaces far from your routine. - **Retries and comparisons become impossible.** A caller who wants to re-run with different parameters, or diff the result against the original, now has to copy defensively first — and if every caller copies, your in-place routine has *increased* total memory use across the system while looking cheaper in isolation. - **Aliasing gets sharp.** If two names refer to the same buffer, or one region overlaps another, an in-place routine can read a cell it has already overwritten. Non-destructive versions are immune by construction. - **Offering both is often right.** A destructive core plus a thin non-destructive wrapper that copies once gives callers the choice, at the cost of one more name to document. The rule of thumb: reach for in-place when the data is large relative to the memory budget, when the caller genuinely has no further use for the original, or when the allocation itself is the bottleneck. Otherwise the copy is cheap insurance, and an interviewer who hears you weigh it that way is hearing production judgment rather than a memorized definition. ## Sound bites that pass, and ones that fail Passes: "In-place means O(1) auxiliary — indices and a swap temporary. I'm also overwriting the caller's buffer, which the contract has to say." Fails: "It's in-place because I didn't create a new array" (said about a routine holding a full-size map); "in-place uses no memory"; "in-place just means it modifies the argument".
- The rescaling fragment above allocates a same-size scratch array. Can it be made genuinely in-place, and what makes that possible?Yes. Each output cell depends only on its own input cell and the total, which is already computed by the end of the first pass — so the second pass can divide each cell in the buffer directly and the scratch disappears, giving O(1) auxiliary. The general condition is that no output position needs an input position that has already been overwritten; when that fails you need either a small rolling window of saved values or an ordering of writes that avoids the conflict.
- Is an algorithm that recurses to depth log n still called in-place?Under the common relaxation, yes — many texts allow O(log n) auxiliary for the call stack so that divide-and-conquer routines can qualify. Under the strict O(1) reading, no. Neither is wrong; what is wrong is leaving it unstated, because the two readings disagree about a widely used class of algorithms. Say "in-place apart from the O(log n) recursion stack" and the ambiguity never arises.
- When is choosing the in-place variant actually the wrong call?When callers still need the original — for retries, for comparison against the result, or because the buffer is shared with another part of the system. If every caller defensively copies before calling you, the in-place routine has raised total memory use while appearing cheaper in isolation. It is also wrong when the data is small relative to the budget and the in-place version is materially harder to read or verify.
saying these in an interview costs you the question
- Says in-place means the algorithm uses no memory at all
- Calls a routine in-place because it modifies its argument
- Ignores a full-size scratch array copied back at the end
- Measures space after the run instead of at peak
- Treats in-place as strictly better with no cost to the caller
- Never mentions that recursion depth counts as space