Sorting in place

The idea of an in-place algorithm isn’t unique to sorting, but sorting is probably the most important case, or at least the most well-known. The idea is about space efficiency – using the minimum amount of RAM, hard disk or other storage that you can get away with. This was especially relevant going back a few decades, when hardware was much more limited.

The idea is to produce an output in the same memory space that contains the input by successively transforming that data until the output is produced. This avoids the need to use twice the storage – one area for the input and an equal-sized area for the output.

Sorting is a fairly obvious case for this because sorting can be done by repeatedly exchanging items – sorting only re-arranges items. Exchanges aren’t the only approach – the Insertion Sort, for example, uses a slightly different approach which is equivalent to doing a run of exchanges but faster.

Another example is matrix transposition – again, this can be implemented by exchanging items. Adding two very large numbers can also be done in-place (the result replacing one of the inputs) by starting at the least significant digit and propogating carries upwards.

Getting back to sorting, the advantages to re-arranging “in place” get even more obvious when you think of stacks of punched cards – it’s preferable to avoid copying punched cards just to sort them.

Some algorithms for sorting allow this style of in-place operation whereas others don’t.

However, all algorithms require some additional storage for working variables. If the goal is simply to produce the output by successively modifying the input, it’s fairly easy to define algorithms that do that by reserving a huge chunk of memory, using that to produce some auxiliary data structure, then using that to guide those modifications. You’re still producing the output by transforming the input “in place”, but you’re defeating the whole point of the exercise – you’re not being space-efficient.

For that reason, the normal definition of an in-place definition requires that you achieve some standard of space efficiency. It’s absolutely not acceptable to use extra space proportional to the input (that is, O(n) extra space) and still call your algorithm “in-place”.

The Wikipedia page on in-place algorithms currently claims that an in-place algorithm can only use a constant amount – O(1) – of extra space.

In computer science, an in-place algorithm (or in Latin in situ) is an algorithm which transforms input using a data structure with a small, constant amount of extra storage space.

There are some technicalities specified in the In Computational Complexity section, but the conclusion is still that e.g. Quicksort requires O(log n) space (true) and therefore is not in-place (which I believe is false).

O(log n) is much smaller than O(n) – for example the base 2 log of 16,777,216 is 24.

Quicksort and heapsort are both normally considered in-place, and heapsort can be implemented with O(1) extra space (I was mistaken about this earlier). Mergesort is more difficult to implement in-place, but the out-of-place version is very cache-friendly – I suspect real-world implementations accept the O(n) space overhead – RAM is cheap but memory bandwidth is a major bottleneck, so trading memory for cache-efficiency and speed is often a good deal.

[EDIT When I wrote the above, I assume I was thinking of in-place merge-sorting of an array. In-place merge-sorting of a linked list is very simple. The key difference is in the merge algorithm – doing a merge of two linked lists with no copying or reallocation is easy, doing the same with two sub-arrays of a larger array (and without O(n) auxiliary storage) AFAIK isn’t.]

Quicksort is also cache-efficient, even in-place, but can be disqualified as an in-place algorithm by appealing to its worst-case behaviour. There is a degenerate case (in a non-randomized version, typically when the input is already sorted) where the run-time is O(n^2) rather than the expected O(n log n). In this case the extra space requirement is also increased to O(n). However, for large datasets and with some basic precautions (mainly randomized pivot selection) this worst-case behaviour becomes absurdly unlikely.

My personal view is that O(log n) extra space is acceptable for in-place algorithms – it’s not cheating as it doesn’t defeat the original point of working in-place.

However, my opinion is of course just my opinion.

One extra note – sometimes, people will call a function in-place simply because it has a single parameter for both the input and the output. It doesn’t necessarily follow that the function was space efficient, that the result was produced by transforming the input, or even that the parameter still references the same area of memory. This usage isn’t correct (or so the prescriptivists will claim), though it’s common enough that it’s best to be aware but not get stressed about it.

Leave a Comment