『嗨威說』算法設計與分析 - STL中Sort函數的實現原理初探

本文索引目錄:

1、對Sort算法實現的我的閱讀體會html

2、Sort算法使用的三個排序算法的優勢介紹算法

  2.1  插入排序的優缺點數組

  2.2  堆排序的優缺點數據結構

  2.3  快速排序的優缺點框架

  2.4  新的結合排序——內省式排序的出現dom

3、sort函數的具體實現函數

4、尾錄oop

 

 

1、對Sort算法實現的我的閱讀體會:

    

    如同《STL源碼剖析》中所說,人類生活在一個有序的世界中,沒有排序,不少事情沒法進展,可是對於排序來講,面對大數據的排序存在着效率的問題。咱們不可能說對十萬個數進行冒泡排序,這在時間成本上是極不現實的。因此,STL爲了追求效率極致,結合三大較爲快速的排序:快排、堆排、插排來實現對大數據的排序。post

    STL是C++很是出色和有較高效率的代碼框架,以優化獲得更高效率爲目標的大做品,更加的體現了C++的高效率。在STL中,有一個排序函數sort無不把這一目標體現得淋淋盡致。爲了獲得更快的排序效率,sort函數根據序列的大小,自動地合理使用快排(Quick Sort)、插排(Insertion Sort)和堆排(Heap Sort)。學習

    STL的開發者還很是有意思,經過多個方面來決定這三個排序的使用,例如當問題規模較少時,爲了減小遞歸函數調用的資源佔用,則採起插排來迅速對小額數據量完成排序,在數據量不大時,插排的速度效率徹底是能夠超過快排的。當問題規模適中時,STL能夠先進行快排,而後當快排遞歸分組的序列組達到必定小的問題規模時,同上實行插排,完成小額排序的任務。當問題規模過大時,STL開發者考慮到了快排調用遞歸層的深度,爲了防止遞歸層過深過多,面對大額數據量時,會改用堆排。

    這或許就是算法工程師的魅力吧,追求高效的算法,也映射出了一些道理,在從此的工做學習生活中,一個高效的工做/生活/學習算法,能給你帶來多大的效益,所以摸索更高效的方法尤其重要。

 

 

2、Sort算法使用的三個排序算法的優勢介紹:

2.1  插入排序的優缺點:

 

    插入排序不適合對於數據量比較大的排序應用。可是,若是須要排序的數據量很小,例如,量級小於千;或者若已知輸入元素大體上按照順序排列,那麼插入排序仍是一個不錯的選擇。插入排序最好時間複雜度爲O(n)、最壞時間複雜度爲O(n^2),空間複雜度爲O(1),屬於穩定排序。

 

 

2.2  堆排序的優缺點:

 

 

     堆排序常常是做爲快速排序最有力的競爭者出現,它們的複雜度都是O(N logN)。可是,有一點它卻比快速排序要好不少:最壞狀況它的複雜度仍然會保持O(N logN),這一優勢對本文介紹的新算法有着巨大的做用。

 

 

2.3  快速排序的優缺點:

 

 

    快速排序的優勢無疑就是在內排序最快的排序了,時間複雜度能夠達到O(NlogN),可是可能因爲不當的pivot選擇,致使其在最壞狀況下複雜度惡化爲O(N2)。另外,因爲快速排序通常是用遞歸實現,咱們知道遞歸是一種函數調用,它會有一些額外的開銷,好比返回指針、參數壓棧、出棧等,在分段很小的狀況下,過分的遞歸會帶來過大的額外負荷,從而拉緩排序的速度。

 

 

2.4  新的結合排序——內省式排序的出現:

    因爲快速排序有着前面所描述的問題,所以Musser在1996年發表了一遍論文,提出了Introspective Sorting(內省式排序)。它是一種混合式的排序算法,集成了前面提到的三種算法各自的優勢:

      =》在數據量很大時採用正常的快速排序,此時效率爲O(logN)。

      =》一旦分段後的數據量小於某個閾值,就改用插入排序,由於此時這個分段是基本有序的,這時效率可達O(N)。

      =》在遞歸過程當中,若是遞歸層次過深,分割行爲有惡化傾向時,它可以自動偵測出來,使用堆排序來處理,在此狀況下,使其效率維持在堆排序的O(N logN),但這又比一開始使用堆排序好。

    由此可知,它乃綜合各家之長的算法。也正由於如此,C++的標準庫就用其做爲std::sort的標準實現。

 

 

3、sort函數的具體實現:

    注意:若要使用sort函數,須要包括頭文件#include<algorithm>。

    在老師推薦的《STL源碼剖析》給出了sort的函數原型:

template <class RandomAccessIterator> inline void sort(RandomAccessIterator first, RandomAccessIterator last) { if (first != last) { __introsort_loop(first, last, value_type(first), __lg(last - first) * 2); __final_insertion_sort(first, last); } }

    咱們能夠發現,sort函數須要針對RandomAccessIterator,這是一個隨機存取迭代器,通常針對數組、結構體數組、類數組、vector容器、deque使用,其餘的容器通常有內置的sort排序算法。

    首先if判斷序列區間的有效性,不是單個對象,接着調用__introsort_loop,在該函數結束以後,最後調用插入排序(__final_insertion_sort)。

    對於__introsort_loop函數STL展現以下:

template <class RandomAccessIterator, class T, class Size>
void __introsort_loop(RandomAccessIterator first,RandomAccessIterator last, T*,Size depth_limit) { while (last - first > __stl_threshold) { if (depth_limit == 0) { partial_sort(first, last, last); return; } --depth_limit; RandomAccessIterator cut = __unguarded_partition(first, last, T(__median(*first, *(first + (last - first)/2),*(last - 1)))); __introsort_loop(cut, last, value_type(first), depth_limit); last = cut; } }

    在這函數中:能夠看出它是一個遞歸函數,由於咱們說過,Introspective Sort在數據量很大的時候採用的是正常的快速排序,所以除了處理惡化狀況之外,它的結構應該和快速排序一致。除此以外,STL還使用了三點中值法來防止排序惡化。

    除了以上兩個特色

      ·遞歸函數

      ·三點中值爲標記

    STL還針對partition分割函數進行了優化,並在書中給出了很是簡單明瞭的圖解:

 

    最後STL還對遞歸深度進行了實時判斷:

      ·遞歸深度

    __introsort_loop函數中的depth_limit是前面所提到的判斷分割行爲是否有惡化傾向的閾值,即容許遞歸的深度,調用者傳遞的值爲2logN。注意看if語句,當遞歸次數超過閾值時,函數調用partial_sort,它即是堆排序,堆排序結束以後直接結束當前遞歸。

      ·最小分段閾值

    除了遞歸深度閾值之外,Introspective Sort還用到另一個閾值。注意看__introsort_loop中的while語句,其中有一個變量__stl_threshold,其定義爲:const int __stl_threshold = 16;

    它就是咱們前面所說的最小分段閾值。當數據長度小於該閾值時,再使用遞歸來排序顯然不划算,遞歸的開銷相對來講太大。而此時整個區間內部有多個元素個數少於16的子序列,每一個子序列都有至關程度的排序,但又還沒有徹底排序,過多的遞歸調用是不可取的。而這種狀況恰好插入排序最拿手,它的效率可以達到O(N)。所以這裏停止快速排序,sort會接着調用外部的__final_insertion_sort,即插入排序來處理未排序徹底的子序列。

 

    對於__final_insertion_sort函數原型展現以下:

template <class RandomAccessIterator, class T>
void __unguarded_linear_insert(RandomAccessIterator last, T value) { RandomAccessIterator next = last; --next; while (value < *next) { *last = *next; last = next; --next; } *last = value; } template <class RandomAccessIterator, class T> inline void __linear_insert(RandomAccessIterator first, RandomAccessIterator last, T*) { T value = *last; if (value < *first) { copy_backward(first, last, last + 1); *first = value; } else __unguarded_linear_insert(last, value); } template <class RandomAccessIterator>
void __insertion_sort(RandomAccessIterator first, RandomAccessIterator last) { if (first == last) return; for (RandomAccessIterator i = first + 1; i != last; ++i) __linear_insert(first, i, value_type(first)); }

    STL的插入排序和咱們以前在數據結構課程中所學的插入排序還略有不一樣,在STL的插入排序中用到了一些很妙的優化效率解決方式,但鑑於個人表達能力有限,爲了避免誤導他人對sort裏插排的理解,就不在此贅述講解。

 

 

4、尾錄:

    很是感謝侯捷的《STL源碼剖析》以及老師推薦的feihu做者,在腦海中或者筆記上總結,和匯聚成文相比徹底是兩回事,有太多的背景要介紹,述說的方式,結構的安排等等無一不需花費心思。如今能夠體會出每一個寫做者的艱辛之處,更對認真完成衆多經典做品的做者們充滿了敬佩。確實如此,就連我在簡單編寫淺顯的博客的時候,都得思考如何排版結構闡述、合理解釋,這都須要花費很多心思和精力,但這些優秀做品無疑能給他人帶來巨大幫助,爲此以這些優秀的做者爲目標,但願可以有機會分享富有高質量高水平的知識點講解給有須要的人。

 

 

 

若有不合理的地方,請及時指正,我願聽取改正~

原文出處:https://www.cnblogs.com/WinniyGD/p/11603777.html

相關文章
相關標籤/搜索