真核硬貨——STL中的sort()函數詳解

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
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章