stl中的sort函式,你真的瞭解嗎
前言
stl中為部分容器準備了sort方法進行排序,但你真的瞭解他嗎?
首先,sort方法使用了哪種演算法來進行排序?
如果你回答:
STL裡的sort演算法肯定用的是快速排序啊?難不成還是氣泡排序麼?
如果你只是回答快速排序,那麼恭喜你只答對了33.333%,離正確答案還差一大截。
因為還有一大串的問題:
資料量大和資料量小都適合用快速排序嗎?
快速排序的時間複雜度不是穩定的nlogn,最壞情況會變成n^2,怎麼解決複雜度惡化問題?
快速排序遞迴實現時,怎麼解決遞迴層次過深的問題?
遞迴過深會引發什麼問題? 怎麼控制遞迴深度?如果達到遞迴深度了還沒排完序怎麼辦?
用到哪種排序演算法,正確答案是:
毫無疑問是用到了快速排序,但不僅僅只用了快速排序,還結合了插入排序和堆排序。
然而並非所有容器都使用sort演算法
既然問的是STL的sort演算法實現,那麼先確認一個問題,哪些STL容器需要用到sort演算法?
首先,關係型容器擁有自動排序功能,因為底層採用RB-Tree,所以不需要用到sort演算法。
其次,序列式容器中的stack、queue和priority-queue都有特定的出入口,不允許使用者對元素排序。
剩下的vector、deque,適用sort演算法。
實現邏輯
STL的sort演算法,資料量大時採用QuickSort快排演算法,分段歸併排序。一旦分段後的資料量小於某個門檻(16),為避免QuickSort快排的遞迴呼叫帶來過大的額外負荷,就改用Insertion
Sort插入排序。如果遞迴層次過深,還會改用HeapSort堆排序。
結合快速排序-插入排序-堆排序 三種排序演算法。
演算法具體實現
具體程式碼
原始檔:https://gcc.gnu.org/onlinedocs/libstdc++/libstdc+±html-USERS-4.4/a01347.html
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);
}
}
其中__lg函式是計算遞迴深度,用來控制分割惡化,當遞迴深度達到該值改用堆排序,因為堆排序是時間複雜度恆定為nlogn:
template<typename _Size>
inline _Size
__lg(_Size __n)
{
_Size __k;
for (__k = 0; __n != 1; __n >>= 1)
++__k;
return __k;
}
先來看,__introsort_loop 快排實現部分:對於區間小於16的採用快速排序,如果遞迴深度惡化改用堆排序。
template<typename _RandomAccessIterator, typename _Size>
void
__introsort_loop(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Size __depth_limit)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;
//_S_threshold=16,每個區間必須大於16才遞迴
while (__last - __first > int(_S_threshold))
{
//達到指定遞迴深度,改用堆排序
if (__depth_limit == 0)
{
std::partial_sort(__first, __last, __last);
return;
}
--__depth_limit;
_RandomAccessIterator __cut =
std::__unguarded_partition(__first, __last,
_ValueType(std::__median(*__first,
*(__first
+ (__last
- __first)
/ 2),
*(__last
- 1))));
std::__introsort_loop(__cut, __last, __depth_limit);
__last = __cut;
}
}
再來看插入排序部分:
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);
}
else
std::__insertion_sort(__first, __last);
}
為什麼用插入排序?因為插入排序在面對“幾近排序”的序列時,表現更好。
總結
一個簡單的sort背後也有開發者仔細的考慮,充分的瞭解函式有助於我們更好地使用。
相關文章
- ViewStub你真的瞭解嗎View
- 你真的瞭解mongoose嗎?Go
- 你真的瞭解 WebSocket 嗎?Web
- 你真的瞭解響應式佈局嗎?
- 你真的對 Linux 中的 Inode 瞭解嗎?Linux
- 你真的懂函式嗎?函式
- 面試官:你真的瞭解Redis分散式鎖嗎?面試Redis分散式
- JavaScript 你真的瞭解this指向嗎JavaScript
- 你真的瞭解前端路由嗎?前端路由
- 你真的瞭解RPC嗎?RPC
- 你真的瞭解URLEncode嗎?
- 你真的瞭解“密碼”嗎?密碼
- 你真的瞭解nosql世界嗎?SQL
- 你真的瞭解深度學習嗎?深度學習
- 你真的瞭解 Cookie 和 Session 嗎?CookieSession
- 你真的瞭解 Cookie 和 Session 嗎CookieSession
- 你真的瞭解npm-scripts嗎?NPM
- 你真的瞭解 Session 和 Cookie 嗎?SessionCookie
- 你真的瞭解js運算子嗎JS
- 你真的瞭解HTTP快取嗎HTTP快取
- css3中的@font-face你真的瞭解嗎CSSS3
- 你真的瞭解HTTP中GET與POST的區別嗎?HTTP
- 注意!JS的結構你真的瞭解嗎?JS
- platform 模組 你真的瞭解你的計算機嗎?Platform計算機
- 你真的瞭解 React 生命週期嗎React
- 你真的瞭解Event Loop(事件環)嗎?OOP事件
- 你真的瞭解 OkHttp 快取控制嗎?HTTP快取
- AnyForWeb分享:畫素!你真的瞭解嗎?Web
- 你真的瞭解python嗎?這篇文章帶你快速瞭解!Python
- 你真的瞭解JS陣列的那些方法嗎?JS陣列
- 你真的瞭解 NDK 和 jni 的區別嗎
- C++的那些事:你真的瞭解引用嗎C++
- 你真的瞭解你的團隊嗎? ONA告訴你真相
- 你真的瞭解資料在堆疊中的儲存方式嗎?
- 你真的瞭解延時佇列嗎(一)佇列
- 靈魂拷問,你真的瞭解DNS嗎?DNS
- 【UI設計師】你真的瞭解色彩嗎?UI
- 你真的瞭解Android金鑰庫嗎?Android