Which type of sorting is used in the std::sort()?
Most implementations of std::sort
use quicksort, (or usually a hybrid algorithm like introsort, which combines quicksort, heapsort and insertion sort).
The only thing the standard requires is that std::sort
somehow sort the data according to the specified ordering with a complexity of approximately O(N log(N)); it is not guaranteed to be stable. Technically, introsort better meets the complexity requirement than quicksort, because quicksort has quadratic worst-case time.
C++ Standard ISO/IEC 14882:2003
25.3.1.1 sort
template<class RandomAccessIterator> void sort(RandomAccessIterator first, RandomAccessIterator last); template<class RandomAccessIterator, class Compare> void sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
1 Effects: Sorts the elements in the range [first, last).
2 Complexity: Approximately N log N (where N == last - first) comparisons on the average.
There is no information about method but complexity is always N log N
.
There are three algorithms that are used in MSVC2013 STL, referring to the source code of std::sort
.
It is most likely to use
QuickSort
, or a variation overQuickSort
calledIntroSort
.If the recursion goes too deep, the
HeapSort
will be used here.Otherwise
InsertSort
will be used.
Probably all implementations of std::sort
use introsort (aka introspection sort), a hybrid algorithm that combines quicksort and heapsort. Actually, introsort was particularly invented in 1997 for the purpose of a performant sort implemenation in C++ STL.
The only thing the standard requires is that std::sort
somehow sort the data according to the specified ordering with a complexity of O(N log(N)); it is not guaranteed to be stable (there is a separate std::stable_sort
algorithms available, if this should be required).
Technically, introsort better meets the complexity requirement than quicksort: This is because heapsort has guaranteed O(N log(N)) complexity in the worst case, whereas quicksort has quadratic worst-case time.
However, heapsort is 'slower' than quicksort in the average case, in the sense that heapsort performs C*N log(N) whereas quicksort has D*N log(n) performance, with D being significantly smaller than C (the numbers C and D are constants). In other words, the per-comparison-overhead of heapsort is higher than the one of quicksort.
To get the best of both worlds, introsort starts with quicksort —a recursive algorithm—, but when recursion depth gets too high (which means it gets into a degenerated worst-case behaviour), it switches to heapsort.
See also the Wikipedia entry for introsort or the original paper from David Musser, who invented introsort particularly for STL.
GCC 9.2.0 libstdc++ source confirming introsort
Others have mentioned introsort, but here is some evidence for libstc++, which is the default C++ implementation on most Linux distros.
libstdc++ is a part of GCC itself, so we will look into GCC source.
libstdc++-v3/include/std/algorithm is the main header and contains:
#include <bits/stl_algobase.h>
#include <bits/stl_algo.h>
Then, bits/stl_algo.h contains the definition of the sort overloads, one of them being:
/**
* @brief Sort the elements of a sequence.
* @ingroup sorting_algorithms
* @param __first An iterator.
* @param __last Another iterator.
* @return Nothing.
*
* Sorts the elements in the range @p [__first,__last) in ascending order,
* such that for each iterator @e i in the range @p [__first,__last-1),
* *(i+1)<*i is false.
*
* The relative ordering of equivalent elements is not preserved, use
* @p stable_sort() if this is needed.
*/
template<typename _RandomAccessIterator>
inline void
sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
// concept requirements
__glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
_RandomAccessIterator>)
__glibcxx_function_requires(_LessThanComparableConcept<
typename iterator_traits<_RandomAccessIterator>::value_type>)
__glibcxx_requires_valid_range(__first, __last);
__glibcxx_requires_irreflexive(__first, __last);
std::__sort(__first, __last, __gnu_cxx::__ops::__iter_less_iter());
}
so we see that this just does a bunch of sanity checks on the input, and then calls std::__sort
which is defined in the same file:
template<typename _RandomAccessIterator, typename _Compare>
inline void
__sort(_RandomAccessIterator __first, _RandomAccessIterator __last,
_Compare __comp)
{
if (__first != __last)
{
std::__introsort_loop(__first, __last,
std::__lg(__last - __first) * 2,
__comp);
std::__final_insertion_sort(__first, __last, __comp);
}
}
and I'll stop here that we have reached an identifier called std::__introsort_loop
, the rest of the implementation is on the same file is anyone still has doubts.
It should also be possible to GDB step debug into std::sort
without any further setup in Ubuntu 18.04 as mentioned for std::set
at: What is the underlying data structure of a STL set in C++?
C++17 parallel sorting
We now also have parallel sorting, so it would be worth looking on how it is done as well: Are C++17 Parallel Algorithms implemented already?
As mentioned in the above answer, the implementation relies on an external library: Intel Thread Building Blocks: https://github.com/intel/tbb