STL的sort()演算法
阿新 • • 發佈:2020-08-19
STL的sort()演算法
靈魂追問
- STL裡sort演算法用的是什麼排序演算法?
- 資料量大和資料量小都適合用快速排序嗎?
- 快速排序的時間複雜度不是穩定的nlogn,最壞情況會變成n^2,怎麼解決複雜度惡化問題?
- 快速排序遞迴實現時,怎麼解決遞迴層次過深的問題?
- 遞迴過深會引發什麼問題?
- 怎麼控制遞迴深度?如果達到遞迴深度了還沒排完序怎麼辦?
sort原始碼
以下程式碼截自vs2019的的algorithm
sort函式原型
可以看到排序是對左閉右開的區間進行,而且預設情況下less<>()
指定內建型別從小到大。
template <class _RanIt, class _Pr> _CONSTEXPR20 void sort(const _RanIt _First, const _RanIt _Last, _Pr _Pred) { // order [_First, _Last), using _Pred _Adl_verify_range(_First, _Last); const auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); _Sort_unchecked(_UFirst, _ULast, _ULast - _UFirst, _Pass_fn(_Pred)); } template <class _RanIt> _CONSTEXPR20 void sort(const _RanIt _First, const _RanIt _Last) { // order [_First, _Last), using operator< _STD sort(_First, _Last, less<>()); }
sort函式實現
template <class _RanIt, class _Pr> _CONSTEXPR20 void _Sort_unchecked(_RanIt _First, _RanIt _Last, _Iter_diff_t<_RanIt> _Ideal, _Pr _Pred) { // order [_First, _Last), using _Pred for (;;) { if (_Last - _First <= _ISORT_MAX) { // small _Insertion_sort_unchecked(_First, _Last, _Pred); return; } if (_Ideal <= 0) { // heap sort if too many divisions _Make_heap_unchecked(_First, _Last, _Pred); _Sort_heap_unchecked(_First, _Last, _Pred); return; } // divide and conquer by quicksort auto _Mid = _Partition_by_median_guess_unchecked(_First, _Last, _Pred); _Ideal = (_Ideal >> 1) + (_Ideal >> 2); // allow 1.5 log2(N) divisions if (_Mid.first - _First < _Last - _Mid.second) { // loop on second half _Sort_unchecked(_First, _Mid.first, _Ideal, _Pred); _First = _Mid.second; } else { // loop on first half _Sort_unchecked(_Mid.second, _Last, _Ideal, _Pred); _Last = _Mid.first; } } }
分析:
- 當區間長度小於一定值時,改用插入排序。vs2019下這個值是32
_INLINE_VAR constexpr int _ISORT_MAX = 32; // maximum size for insertion sort
再來看看插入排序的實現,使用了移動語義來替代拷貝
// FUNCTION TEMPLATE sort template <class _BidIt, class _Pr> _CONSTEXPR20 _BidIt _Insertion_sort_unchecked(_BidIt _First, const _BidIt _Last, _Pr _Pred) { // insertion sort [_First, _Last), using _Pred if (_First != _Last) { for (_BidIt _Next = _First; ++_Next != _Last;) { // order next element _BidIt _Next1 = _Next; _Iter_value_t<_BidIt> _Val = _STD move(*_Next); if (_DEBUG_LT_PRED(_Pred, _Val, *_First)) { // found new earliest element, move to front _Move_backward_unchecked(_First, _Next, ++_Next1); *_First = _STD move(_Val); } else { // look for insertion point after first for (_BidIt _First1 = _Next1; _DEBUG_LT_PRED(_Pred, _Val, *--_First1); _Next1 = _First1) { *_Next1 = _STD move(*_First1); // move hole down } *_Next1 = _STD move(_Val); // insert element in hole } } } return _Last; }
至於為什麼選擇快排,因為經過之前的快排,資料已經相對有序。
- 使用
_Ideal
來控制遞迴深度,當快排的初始序列是逆序時,複雜度是O(N2)。變數_Ideal
初始化為區間長度,每一次劃分會執行
_Ideal = (_Ideal >> 1) + (_Ideal >> 2); // allow 1.5 log2(N) divisions
當_Ideal <= 0
會進行堆排序,至於為什麼選擇堆排序,因為複雜度穩定為O(Nlog2N)。
注:
不是所有STL容器都適合sort()。首先,關係型容器底層是紅黑樹,自動排序所以不需要。其次,棧和優先佇列等限制出入口的容器不允許排序。