sort函数

1. 背景

  • 快速排序虽然平均复杂度为O(N logN)

    • 却可能由于不当的pivot选择,导致其在最坏情况下复杂度恶化为O(N2)。

    • 另外,由于快速排序一般是用递归实现,过度的递归会带来过大的额外负荷,从而拉缓排序的速度。

  • 堆排序经常是作为快速排序最有力的竞争者出现,平均时间上,堆排序的时间常数比快排要大一些,因此通常会慢一些,但是堆排序最差时间也是O(nlogn)的,这点比快排好。

2. 实现

std::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);
     }
 } // 它是一个模板函数,只接受随机访问迭代器。

在数据量很大的时候采用的是正常的快速排序,但是每次划分子序列之后仅对右子序列进行函数调用,左边子序列进行正常的循环调用。

两者区别就在于STL节省了接近一半的函数调用,由于每次的函数调用有一定的开销,因此对于数据量非常庞大时,这一半的函数调用可能能够省下相当可观的时间。

  • 三点中值法:

其中有一个__median函数,它的作用是取首部、尾部和中部三个元素的中值作为pivot。我们之前学到的快速排序都是选择首部、尾部或者中间位置的元素作为pivot,并不会比较它们的值,在很多情况下这将引起递归的恶化。现在这里采用的中值法可以在绝大部分情形下优于原来的选择。

  • 递归深度阈值:

当递归次数超过阈值时,函数调用partial_sort,它便是堆排序。

如前所述,此时采用堆排序可以将快速排序的效率从O(N2)提升到O(N logN),杜绝了过度递归所带来的开销。堆排序结束之后直接结束当前递归。

  • 最小分段阈值:

 const int __stl_threshold = 16;

它就是我们前面所说的最小分段阈值。当数据长度小于该阈值时,再使用递归来排序显然不划算,递归的开销相对来说太大。而此时整个区间内部有多个元素个数少于16的子序列,每个子序列都有相当程度的排序,但又尚未完全排序,过多的递归调用是不可取的。而这种情况刚好插入排序最拿手,它的效率能够达到O(N)。

posted @ 2024-04-23 16:32  小熊酱  阅读(1)  评论(0编辑  收藏  举报