std::sort算法原理

前一篇博客将主流的一些排序算法的性能进行了比较,于是抱着好奇的心态想了解了解c++标准库中std::sort采用的排序方法.

std::sort基本框架

基本框架如下图所示:

  1. 判断输入数据个数是否小于给定阈值,如果是,则直接进行插入排序
  2. 否则,判断递归深度是否小于阈值,如果是,进行快速排序
  3. 否则,进行堆排序
  4. 循环1,2,3
    在这里插入图片描述

源码剖析

  template<typename _RandomAccessIterator, typename _Compare>
    inline void __sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
    {
	    if (__first != __last)
		{
		  std::__introsort_loop(__first, __last,
					std::__lg(__last - __first) * 2,
					__comp);
		  std::__final_insertion_sort(__first, __last, __comp);
		}
    }

其中__lg()函数实现如下,其结果用来控制分割恶化的情况,即框图中的第二个递归深度阈值.

template <class Size>
inline Size __lg(Size n){
	Size k;
	for (k = 0; n > 1; n >>= 1) ++k;
	return k;
}

__introsort_loop()函数实现如下:

    template<typename _RandomAccessIterator, typename _Size, typename _Compare>
    void __introsort_loop(_RandomAccessIterator __first,  _RandomAccessIterator __last, 
    	_Size __depth_limit, _Compare __comp)
    {
      while (__last - __first > int(_S_threshold)) //判断元素个数是否超过给定的阈值16,如果否,则直接跳出该函数,直接执行插入排序
	{
	  if (__depth_limit == 0) //判断递归深度是否超过__lg()函数计算出来的阈值,如果是则直接进行堆排序
	    {
	      std::__partial_sort(__first, __last, __last, __comp); //堆排序
	      return;
	    }
	  --__depth_limit;
	  _RandomAccessIterator __cut =
	  std::__unguarded_partition_pivot(__first, __last, __comp); //进行快速排序的切分
	  std::__introsort_loop(__cut, __last, __depth_limit, __comp); //递归进行快速排序
	  __last = __cut;
	}
    }

__unguarded_partition_pivot()函数实现如下

  template<typename _RandomAccessIterator, typename _Compare>
    inline _RandomAccessIterator __unguarded_partition_pivot(_RandomAccessIterator __first,
	_RandomAccessIterator __last, _Compare __comp)
    {
      _RandomAccessIterator __mid = __first + (__last - __first) / 2;
      std::__move_median_to_first(__first, __first + 1, __mid, __last - 1, __comp); //使用三点(起点,中点,末尾点)中值作为切分点
      return std::__unguarded_partition(__first + 1, __last, __first, __comp); //进行快速排序的切分(通用的切分方法)
    }

说明

  1. 为什么元素个数较少时直接采用插入排序?
    因为当元素个数少于阈值时,该子序列都有相当程度的排序,但尚未完全排序.插入排序有个很好的特性就是对于几近排序完的数据,有很好的表现
  2. 为什么递归深度超过阈值直接采用堆排序?
    递归太深容易导致栈溢出问题,为了避免该问题,当递归深度太深时直接调用堆排序,原因是因为堆排序是原地排序,并且运行时间复杂度也是nlogn,速度很快.
  3. 为什么要用三中值查切分点?
    快速排序有个很大的缺点就是当切分点选择不合理,数据不能切分平衡的话,导致性能最差可能会退化到O(N^2),使用三种值确定切分点,可以较好的避免缺点的出现

参考

<<STL源码剖析>>
<<算法第四版>>

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值