Systems & Performance

In-Place vs Out-of-Place Sorting

What in-place sorting really means, why O(1) and O(log n) both count, which algorithms qualify, and the trade-off between memory, stability and speed.

Aman Jaiman
9 min read
On this page +
  1. In plain English
  2. The definition, and the part people get wrong
  3. Which algorithms are which
  4. The trade-off you are actually making
  5. When this actually decides something
  6. How to talk about it precisely
  7. Frequently asked questions

An in-place sorting algorithm rearranges elements within the array it was given, using only a constant or logarithmic amount of extra memory. An out-of-place one allocates additional space proportional to the input — typically a second array as large as the first.

It sounds like a footnote, and on a laptop sorting a thousand items it is. It stops being a footnote when you are sorting a large fraction of available memory, working on an embedded device, or wondering why the algorithm with the better guarantees is not the one your language chose. And because it trades directly against stability, understanding it explains most of the design decisions in real sorting libraries.

The definition, and the part people get wrong #

Every sorting algorithm needs the array itself, so "space complexity" always means auxiliary space — the scratch memory needed on top of the input. Strictly, in-place means O(1) auxiliary space: a handful of loop counters and temporaries, whatever the input size.

The part that trips people up is recursion. Quick Sort allocates no arrays, but each recursive call consumes a stack frame, and with balanced partitions the recursion is O(log n) deep. So Quick Sort's auxiliary space is O(log n), not O(1).

By convention it is still called in-place, because O(log n) is negligible: for a billion elements, log₂n is only about 30 frames. In interviews, say "in-place, with O(log n) stack space for the recursion" — it is precise, and it signals that you thought about the stack rather than only about arrays.

One more wrinkle worth knowing: the stack depth depends on the partitioning. If you always recurse into the smaller partition first and loop on the larger one (tail-call elimination), the depth is bounded at O(log n) even in the worst case. Without that, a degenerate Quick Sort can recurse O(n) deep and overflow the stack — which is a crash, not just a slowdown.

Which algorithms are which #

In-place, O(1) auxiliary space:

  • Bubble Sort — swaps adjacent pairs, needs one temporary.
  • Selection Sort — tracks a minimum index and swaps.
  • Insertion Sort — shifts elements right and drops one value back in.
  • Shell Sort — gapped insertion sort; still just shifting.
  • Heap Sort — builds the heap inside the array itself, iteratively.

In-place by convention, O(log n) stack:

  • Quick Sort — partitions in place, recursion costs stack frames.
  • Introsort — Quick Sort plus Heap Sort, so the same bound.

Out-of-place:

A pattern worth noticing: Heap Sort is the only algorithm in the top group that is also O(n log n) in the worst case. That combination — guaranteed time and constant space — is exactly why it survives despite being slower than Quick Sort in practice.

The trade-off you are actually making #

Extra memory is not wasted; it buys specific properties.

Out-of-place buys stability. Merging two runs into a buffer makes preserving the order of equal elements trivial: when values tie, always take from the left run. Doing the same in place requires moving elements over long distances, and long-distance moves are what destroy relative order. This is why almost every stable algorithm either uses a buffer (Merge, Tim, Counting, Radix) or only ever shifts by one position (Insertion, Bubble), and why every unstable one (Quick, Heap, Selection, Shell) does long-distance swaps.

Out-of-place buys simpler correctness. Merge Sort's guarantee of O(n log n) in all cases is easier to achieve when you are not fighting for space. In-place merge algorithms exist, but they are intricate and slower by a large constant — enough that nobody uses them in practice.

In-place buys cache locality and allocation-free operation. One region of memory means less cache pressure, which is a large part of why Quick Sort outruns Merge Sort in RAM. It also means no allocation, which matters when allocation can fail or stall.

So the choice is roughly: constant memory and raw speed, or stability and predictability. You do not usually get both. The exception is Heap Sort, which gives you constant memory and a worst-case guarantee — and pays for it by being unstable and cache-hostile.

When this actually decides something #

Most of the time you can ignore it. These are the cases where you cannot.

Sorting a large fraction of available memory. If a dataset occupies 60% of your RAM, an O(n) auxiliary buffer needs another 60% and the sort fails or starts swapping. Heap Sort will finish. Beyond that, the answer is external sorting.

Embedded and real-time systems. A microcontroller with a few kilobytes of RAM cannot allocate a second array, and many real-time systems forbid dynamic allocation entirely because it makes timing unpredictable. Heap Sort and Shell Sort are common here — Shell Sort especially, because it is also non-recursive, so it needs no meaningful stack either.

Deep recursion risk. On a thread with a small stack, an unguarded Quick Sort on adversarial input can recurse O(n) deep and overflow. This is a crash rather than a slow query, which makes it worse than the O(n²) time it is usually discussed alongside. Recursing into the smaller side first fixes it.

Kernel and allocator code. Code that runs below the allocator cannot call it, so in-place is not a preference but a requirement.

Linked lists invert the usual answer. Merge Sort on a linked list needs no buffer at all — merging is pointer rewiring — so it is O(1) auxiliary space there, while Quick Sort loses the random access it depends on. The algorithm labelled "out-of-place" becomes the in-place one, purely because the data structure changed.

How to talk about it precisely #

A few phrasings that hold up under follow-up questions.

Say auxiliary space rather than "space", because it makes clear you are not counting the input. Say "in-place with O(log n) stack space" for Quick Sort rather than claiming O(1). Note that Heap Sort is the only common algorithm with both O(n log n) worst case and O(1) space — it is the answer to "when would you use Heap Sort at all?". And mention that Tim Sort's O(n) figure is a worst case; its merges frequently need far less, which is part of why it is acceptable as a default despite being out-of-place.

If any of this feels abstract, the visualiser makes it visible: watch Insertion Sort and you will see values shift one slot at a time within the single row of bars, and watch Merge Sort and you will see whole blocks rewritten at once — that rewriting is the buffer doing its work.

Found this useful?

Share it with someone who is learning this too.

Questions

Frequently asked questions

What does in-place sorting mean? +
It means the algorithm rearranges elements inside the array it was given, using only a constant amount of extra memory for temporaries and counters. Space complexity always refers to this auxiliary space, not to the array itself.
Is Quick Sort in-place? +
By convention yes, though strictly it uses O(log n) auxiliary space for its recursion stack rather than O(1). It allocates no arrays; the space comes from stack frames. The precise phrasing is 'in-place with O(log n) stack space'.
Which sorting algorithms are in-place? +
Bubble, Selection, Insertion, Shell and Heap Sort use O(1) auxiliary space. Quick Sort and Introsort use O(log n) for recursion and are usually counted as in-place. Merge, Tim, Counting, Radix and Bucket Sort are out-of-place.
Why is Merge Sort not in-place? +
Its merge step combines two sorted runs by writing the result somewhere, which requires a buffer as large as the region being merged — O(n) overall. In-place merge algorithms exist but are complex and much slower in practice, so implementations use the buffer. On linked lists, however, merging is just pointer rewiring and needs no extra space.
Does in-place sorting mean it is faster? +
Often, but not because of the memory saving itself. In-place algorithms touch one region of memory, which improves cache locality and avoids allocation, and that is a large part of why Quick Sort beats Merge Sort in RAM. But Heap Sort is in-place and slower than Quick Sort, so the property alone does not guarantee speed.

See it in motion

Watch this algorithm and nine others run step by step, with live pseudocode and comparison counters.

Launch the visualiser
Aman Jaiman
Written by
Aman Jaiman

Software engineer at a stealth-stage startup, and previously a front-end engineer for around a year and a half.

Keep reading

Related guides

Link copied