sort()函數是STL中我們常用到的一個函數,之前好像在哪裏模模糊糊的看到過是用快速排序進行排序的
但底層究竟是怎樣進行排序的,今天我們就來進行一個探索
這裏先給出一個結論:STL中的sort()函數的排序方法是以快速排序+堆排序+插入排序爲組合的排序
STL中所有的關係型容器都擁有自動排序功能(底層結構採用RB-tree),所以不需要用到這個sort算法
首先sort()函數的主體軀幹是快速排序,我們先來看一看快速排序的優劣,已經改良方式
下圖是一個標準的常見快速排序
快排的優點是對於數據量的數據處理的時候速度很快,但是對於小數據量的數據處理起來並不具優勢
總的來說,對於快速排序算法的改進主要集中在三個方面:
1 選取一個更好的中軸值
2 根據產生的子分區大小調整算法
3 不同的劃分分區的方法
sort()函數對於快速排序的改進也就剛好體現在了這三點上
選取一個更好的中軸值
這裏STL採用了一個Median-of-Three(三點中值)的方法進行選值
簡單描述就是比較隊首隊尾和隊中三個值,選一箇中間值作爲中軸值,這樣排序結果產生的分區就更加均勻,更具效率
根據產生的子分區大小調整算法&&不同的劃分分區的方法
針對於第二點和第三點STL也提出了兩項優化
- 在quicksort將數據分割爲一個較小容量的range後採用在少量數據排序中效率更加出色的插入排序進行排序(幾近排序但尚未完成的狀態時,插入排序的效率要高於任何其他的排序方法,所以選擇該排序方法)
- 利用堆排序處理那些分割後快速排序惡化爲O(N^2)行爲的情況
以下是部分SGI STL sort()源代碼
template <class _RandomAccessIter>
inline void sort(_RandomAccessIter __first, _RandomAccessIter __last) {
__STL_REQUIRES(_RandomAccessIter, _Mutable_RandomAccessIterator);
__STL_REQUIRES(typename iterator_traits<_RandomAccessIter>::value_type,
_LessThanComparable);
if (__first != __last) {
__introsort_loop(__first, __last,
__VALUE_TYPE(__first),
__lg(__last - __first) * 2);
//這是一個插入排序,不做說明
__final_insertion_sort(__first, __last);
}
}
introsort()
這個是一種混合式排序算法,絕大多數情況下與Quick Sort相同,當分割行爲出現二次分割惡化時,其可以自我轉化成爲Heap Sort,效率高於Quick Sort和Heap Sort中的任意一種排序,_log()是用來控制惡化的情況的
template <class Size>
inline Size __lg(Size n) {
Size k;
for (k = 0; n > 1; n >>= 1) ++k;
return k;
}
即求lg(n)
(取下整),意味着快速排序的遞歸調用最多 2*lg(n) 層
loop()函數
template <class _RandomAccessIter, class _Tp, class _Size>
void __introsort_loop(_RandomAccessIter __first,
_RandomAccessIter __last, _Tp*,
_Size __depth_limit)
{
//這裏的__stl_threshold是一個預定義好的值,const int 16
while (__last - __first > __stl_threshold) {
//自此分割惡化
if (__depth_limit == 0) {
//這裏是一個堆排序,不做過多詳解
partial_sort(__first, __last, __last);
return;
}
--__depth_limit;
_RandomAccessIter __cut =
__unguarded_partition(__first, __last,
_Tp(__median(*__first,
*(__first + (__last - __first)/2),
*(__last - 1))));//三點取中值
//對右半段遞歸進行sort
__introsort_loop(__cut, __last, (_Tp*) 0, __depth_limit);
__last = __cut;
//進入循環 對右邊進行遞歸sort
}
}