STL中sort算法简析
生活随笔
收集整理的這篇文章主要介紹了
STL中sort算法简析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
STL里sort算法簡析
文章目錄
- STL里sort算法簡析
- 一、引入
- 二、正解
- 三、源碼
- **`sort的源碼`**:
- **`其中,__introsort_loop便是內省式排序:`**
- 插入排序
- **快速排序實現代碼**
- 函數__lg()防止棧溢出
一、引入
如果面試官給你出了一道面試題:STL里sort算法用的是什么排序算法?
- 當你第一眼看到這道面試題是不是心里在暗喜,因為排序講的是效率,時間復雜度越小越好,并且作為STL的標準算法,更因該注重效率,這時你的腦海里想的肯定是快排
- 此時如果你回答:
- STL里的sort算法肯定用的是快速排序啊?難不成還是冒泡排序么?那么恭喜你,你可能錯失了大廠的機會,如果面試官不提醒你,你可能覺得自己面的還可以
- 嘿嘿😁~~~
如果你回答的是快排并且面試官沒有放棄你的話,你可能會遇到如下問題:
- 數據量大和數據量小都適合用快速排序嗎?
- 快速排序的時間復雜度不是穩定的nlogn,最壞情況會變成n^2,怎么解決復雜度惡化問題?
- 快速排序遞歸實現時,怎么解決遞歸層次過深的問題?
- 遞歸過深會引發什么問題?
- 怎么控制遞歸深度?如果達到遞歸深度了還沒排完序怎么辦?
如果面試官問你了,那么說明你的大廠offer正在向你靠近,因為這些問題并不是你不會,只不過是沒沒把重心放在那方面而已
二、正解
- 1.sort函數提供了兩個版本
- sort(first, last):默認按照小于方式排序,排序結果為升序,一般用排內置類型數據
- sort(first, last, comp):可以通過comp更改元素比較方式,即可以指定排序的結果為升序或者降序,一般以仿函數對象和函數指針的方式提供
- 2.sort并不是一種排序算法,而是將多個排序算法混合而成
- 3. 當元素個數少于__stl_threshold閾值時(16),使用直接插入排序處理
- 4. 當元素個數超過__stl_threshold時,考慮是否能用快排的方式排序,因為當元素數量達到一定程度,遞歸式的快排可能會導致棧溢出而崩,因此:
- 通過__lg函數判斷遞歸的深度
- 5.如果遞歸的深度沒有超過2*lg(n) 時,則使用快排方式排序,注意:快排時使用到了三數取中法預防分割后一邊沒有數據的極端情況
- 如果遞歸深度超過2*lg(n) 時,說明數據量大,遞歸層次太深,可能會導致棧溢出,此時使用堆排序處理
是不是很驚喜,很意外?
為什么?直接看STL源碼實現,來源于侯捷老師翻譯的鼎鼎大名的 《STL源碼剖析》 關于sort算法實現的細節,實現細節有很多精彩的地方。
總結:sort算法用到了快速排序,但不僅僅只用了快速排序,還結合了插入排序和堆排序。
三、源碼
sort的源碼:
template<typename _RandomAccessIterator>inline void sort(_RandomAccessIterator __first, _RandomAccessIterator __last){typedef typename iterator_traits<_RandomAccessIterator>::value_type_ValueType;// concept requirements__glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<_RandomAccessIterator>)__glibcxx_function_requires(_LessThanComparableConcept<_ValueType>)__glibcxx_requires_valid_range(__first, __last);if (__first != __last){//快速排序+插入排序std::__introsort_loop(__first, __last,std::__lg(__last - __first) * 2);//插入排序std::__final_insertion_sort(__first, __last);}}其中,__introsort_loop便是內省式排序:
template <class _RandomAccessIter, class _Tp, class _Size>void __introsort_loop(_RandomAccessIter __first,_RandomAccessIter __last, _Tp*,_Size __depth_limit) {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))));__introsort_loop(__cut, __last, (_Tp*)0, __depth_limit);__last = __cut;}}- 這里進來之后會 首先進行判斷元素是否大于__stl_threshold, __stl_threshold是一個常量值是16,意思就是說我傳入的元素規模小于我們的16的時候我們就沒必要采用我們的內省式算法,直接退回去采用我們的插入排序。
- 這里能看到我們的內省式算法結束之后就是我們的插入排序函數。
插入排序
template<typename _RandomAccessIterator>void__final_insertion_sort(_RandomAccessIterator __first,_RandomAccessIterator __last){if (__last - __first > int(_S_threshold)){//先排前16個std::__insertion_sort(__first, __first + int(_S_threshold));//后面元素遍歷插入到前面有序的正確位置 std::__unguarded_insertion_sort(__first + int(_S_threshold), __last);}elsestd::__insertion_sort(__first, __last);}- 如果說我們的 元素規模大于我們的16了,那就需要去判斷如果我們是不是能采用快速排序,怎么判斷呢?快排是使用遞歸來實現的,如果說我們進行判斷我們的遞歸深度沒有到達我們的最深層次上邊的函數已經看過了,最深是2*lg(n)。那我們就去使用我們的快速排序來進行排序
快速排序實現代碼
template <class _RandomAccessIter, class _Tp>_RandomAccessIter __unguarded_partition(_RandomAccessIter __first, _RandomAccessIter __last,_Tp __pivot) {while (true) {while (*__first < __pivot)++__first;--__last;while (__pivot < *__last)--__last;if (!(__first < __last))return __first;iter_swap(__first, __last);++__first;} }- 如果說大于我們的最深層次的話,這里就會采用我們的堆排序,進行排序。
- 當進行完這個過程的時候要明白,這里并不是說整個傳進來的數組都是這個過程,這里我們是進行的一次排序,他可能是我傳入的數組,也可能是我在進行快排分割之后的左半部分,排序結束之后再去排我的右半部分,所以可以看上邊我們的元素規模是不是大于__stl_threshold這個閾值的時候是一個while循環。
函數__lg()防止棧溢出
其第三個參數中所調用的函數__lg()便是用來控制分割惡化情況。代碼如下:
template <class Size>inline Size __lg(Size n) {Size k;for (k = 0; n > 1; n >>= 1) ++k;return k;}總結
以上是生活随笔為你收集整理的STL中sort算法简析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python中菱形继承的MRO顺序及pr
- 下一篇: STL中的空间配置器