真核硬货——STL中的sort()函数详解

sort()函数是STL中我们常用到的一个函数,之前好像在哪里模模糊糊的看到过是用快速排序进行排序的

但底层究竟是怎样进行排序的,今天我们就来进行一个探索

这里先给出一个结论:STL中的sort()函数的排序方法是以快速排序+堆排序+插入排序为组合的排序

STL中所有的关系型容器都拥有自动排序功能(底层结构采用RB-tree),所以不需要用到这个sort算法

首先sort()函数的主体躯干是快速排序,我们先来看一看快速排序的优劣,已经改良方式

下图是一个标准的常见快速排序

真核硬货——STL中的sort()函数详解_第1张图片

 

快排的优点是对于数据量的数据处理的时候速度很快,但是对于小数据量的数据处理起来并不具优势

总的来说,对于快速排序算法的改进主要集中在三个方面:

    1 选取一个更好的中轴值

    2 根据产生的子分区大小调整算法

    3 不同的划分分区的方法

 

sort()函数对于快速排序的改进也就刚好体现在了这三点上

选取一个更好的中轴值

这里STL采用了一个Median-of-Three(三点中值)的方法进行选值

简单描述就是比较队首队尾和队中三个值,选一个中间值作为中轴值,这样排序结果产生的分区就更加均匀,更具效率

 

根据产生的子分区大小调整算法&&不同的划分分区的方法

针对于第二点和第三点STL也提出了两项优化

  • 在quicksort将数据分割为一个较小容量的range后采用在少量数据排序中效率更加出色的插入排序进行排序(几近排序但尚未完成的状态时,插入排序的效率要高于任何其他的排序方法,所以选择该排序方法)
  • 利用堆排序处理那些分割后快速排序恶化为O(N^2)行为的情况

 

以下是部分SGI STL sort()源代码

template 
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 
inline Size __lg(Size n) {
    Size k;
    for (k = 0; n > 1; n >>= 1) ++k;
    return k;
}

即求lg(n)(取下整),意味着快速排序的递归调用最多 2*lg(n) 层

 

loop()函数

template 
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
  }
}

你可能感兴趣的:(C++知识总结)