Interactive demos require Javascript. Anything else works.

Sorting STL Containers

posted by Stephan Brumme, updated

std::sort

Yes, there is std::sort. Yes, it's pretty much the most efficient way to sort generic STL containers. No, it's not perfect.

Improving std::sort

What if the data is already almost sorted ? std::sort doesn't benefit a lot from processing already sorted data. However, it's quite common to have (partially) sorted data.

std::sort requires RandomAccessIterator which are sometimes not available for custom containers.

What about std::list ? The syntax is completely different: That's why I wrote some sorting routines on my own to overcome all those limitations: The sorting algorithms are ordered more or less by overall performance (slowest first). As you can see faster algorithms have stricter requirements:
Algorithm Stable ? Iterator Notes
Bubble Sort yes forward iterator code shown here requires bidirectional iterator, just remove both instances of --last; (will be slower then)
Selection Sort no forward iterator
Insertion Sort yes bidirectional iterator
Shell Sort no bidirectional iterator std::distance and std::advance benefit heavily from random access iterators
Heap Sort no random-access iterator my n-ary Heap Sort implementation works with bidirectional iterators, too
Merge Sort yes random-access iterator requires additional memory
Merge Sort (in-place) yes forward iterator no additional memory but slower than normal merge sort
Quick Sort no bidirectional iterator std::distance and std::advance benefit heavily from random access iterators
Intro Sort no bidirectional iterator std::distance and std::advance benefit heavily from random access iterators
std::sort no random-access iterator
std::stable_sort yes random-access iterator
The algorithms are nothing new, they can be found in every decent book on algorithms and, of course, online (e.g. Wikipedia).

The live test shows that under certain circumstances even a simple bubble-sort can outperform std::sort.

Live Tests

My server (2.5GHz Quad-Xeon, GCC -O3) sorts several typical containers instantly while you type in a container size.
Javascript/Ajax needs to be enabled. The containers are identical for each sorting algorithm, even the Random Data containers.
Repeated sorts of the same container size may produce slightly different timings depending on the overall server load.

Container Size: (max. 1000000, some restrictions above 30000)

For complex data types the invokation of operator= and operator< might be expensive.
For example, copying a std::string often includes a slow memory allocation.

This little interactive test let's you play around with the various algorithms.

Container Size: (max. 10000)

comp. stands for comparisons:

Update October 13, 2013: You can supply your own comparison functors to each sorting routine (default is std::less of course). If your compiler supports C++11, std::move will be used where appropriate (scroll down to the last section for details).

Update February 24, 2014: I added Merge Sort and Heap Sort (incl. n-ary Heap Sort).

Update November 20, 2020: I added Intro Sort and switched completely to C++11.

Download  sort.h
Latest release: November 20, 2020, size: 16.1 kBytes, 607 lines

CRC32: 3b538aa5
MD5: 34a22fa244b88f52e63109e427308589
SHA1: d210d460f125ff1505d3e43ead9bce491ff315f4
SHA256:b81426d097cb20c73b4f2d5be5ff2dd1eea8dce76c500afa0ff3b95aec4734c7

If you encounter any bugs/problems or have ideas for improving future versions, please write me an email: create@stephan-brumme.com

License

This code is licensed under the zlib License:
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.zlib License

Changelog

Bubble Sort

Bubble sort is the most simple sorting algorithm.
It's actually not that bad if you sort only a few elements or if they are almost sorted.

To boost bubble sort's performance it's essential to include two optimizations:
  1. After each pass, the last element is in its final position. Therefore we can decrement the end iterator after each pass.
  2. After each pass, we check whether we swapped any elements. If not, the container is sorted.
Especially the second optimization gives a huge speed-up on almost sorted containers.

Selection Sort

Selection sort does have a constant runtime, independent on the previous sorting order of the container. It has the lowest number of writes among the algorithms shown here (but is not optimal while Cycle sort is). It's a good compromise for sorting on a Flash/EEPROM device.

Insertion Sort

Insertion sort beats bubble sort in pretty much every aspect. The only reason to prefer bubble sort over insertion sort is that I can write a bug-free bubble sort even after three beers.
Somehow I fail to do the same with insertion sort even though it's not that complex, too.

Shell Sort

Shell sort is based on insertion sort. It's faster when working with large containers but slower when the data is pre-sorted.

Heap Sort

A short version of Heap sort can be written in just two lines of C++ code because of the power of the STL:

n-ary Heap Sort

STL needs a random-access iterator. It's heap is a 2-ary heap, that means each node has at most two children. I wanted to explore the performance behavior of n-ary heap sort where each node may have more than two children. An 8-ary heap seems to be the sweet spot for integers. Larger values need an excessive amount of comparisons during traversal while smaller don't fully exploit the performance gain of data locality.

In contrast to the STL based heap sort, a simpler bidirectional iterator is sufficient for my naryHeapSort code: The unusual local struct is pretty much the same as a C++11 lambda. I could have written an external function, too, but I wanted to keep the namespace clean.

Merge Sort

Unlike all other sorting algorithm shown here, Merge sort requires significant additional memory. To overcome Merge Sort's limitations, I wrote an in-place version - that means, aside from a few variables no additional memory need.
It only differs in the final merge step which needs only a forward iterator.
The in-place Merge Sort outperforms the original version when the container is already sorted but falls behind in pretty much every other situation.
Nevertheless, it's still faster than any other algorithm for forward iterators.

Quick Sort

Quick sort is often a major part of stl::sort.

Intro Sort

Intro sort is a modified QuickSort which switches to Insertion Sort of the number of elements is small (e.g. 16).

Additional Test Code

I wanted to restrict my code to use only what is really needed. Therefore I wrote a short helper class that implements only the less-than operator. All sort algorithms shown on this page successfully operate on such a trimmed down data type, too: Similar code was written to ensure that my routines accept forward and/or bidirectional iterators - unlike std::sort which requires random-access iterators (click to open). The second live test (the one that counts comparisons and assignments) operates on a custom data type (again, click to open):
homepage