====== 11.11 日更新

今天上午来用Google的 benchmark 库测了一下这几个排序的算法的渐进复杂度系数,环境是在 VBox debian 虚拟机里,分了两个核。

这是插入排序的渐进复杂度是 0.16 N^2,以及系数的标准差是 0.07

这是归并排序的渐进复杂度是 11.58 NlgN,以及系数的标准差是 0.03

这是快速排序的渐进复杂度是 6.98 NlgN,以及系数的标准差是 0.02

这是std::sort 排序的渐进复杂度是 4.87 NlgN,以及系数的标准差是 0.02

可以看出 std::sort 拥有更小的渐进复杂度系数

================== 原答案

首先,STL的sort 不仅仅是快排,所以你只是手写快排,哪怕是尾递归式的快排,也不会有它快。

它会先走快排主递归逻辑,但是递归深度是有限的,而且当子区间够小时,会走插入排序,我摘了标准库里几行代码,具体直接看代码就可以了。

std::sort
  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);
	}
    }


STL 里快排尾递归主逻辑
  /// This is a helper function for the sort routine.
  template<typename _RandomAccessIterator, typename _Size>
    void
    __introsort_loop(_RandomAccessIterator __first,
		     _RandomAccessIterator __last,
		     _Size __depth_limit)
    {
      while (__last - __first > int(_S_threshold))
	{
	  if (__depth_limit == 0)
	    {
	      _GLIBCXX_STD_A::partial_sort(__first, __last, __last);
	      return;
	    }
	  --__depth_limit;
	  _RandomAccessIterator __cut =
	    std::__unguarded_partition_pivot(__first, __last);
	  std::__introsort_loop(__cut, __last, __depth_limit);
	  __last = __cut;
	}
    }

部分排序中使用堆排序
  template<typename _RandomAccessIterator>
    inline void
    partial_sort(_RandomAccessIterator __first,
		 _RandomAccessIterator __middle,
		 _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, __middle);
      __glibcxx_requires_valid_range(__middle, __last);

      std::__heap_select(__first, __middle, __last);
      std::sort_heap(__first, __middle);
    }

快排中的partion,这里用的是三分取中
  template<typename _RandomAccessIterator>
    inline _RandomAccessIterator
    __unguarded_partition_pivot(_RandomAccessIterator __first,
				_RandomAccessIterator __last)
    {
      _RandomAccessIterator __mid = __first + (__last - __first) / 2;
      std::__move_median_first(__first, __mid, (__last - 1));
      return std::__unguarded_partition(__first + 1, __last, *__first);
    }

  template<typename _RandomAccessIterator, typename _Tp>
    _RandomAccessIterator
    __unguarded_partition(_RandomAccessIterator __first,
			  _RandomAccessIterator __last, const _Tp& __pivot)
    {
      while (true)
	{
	  while (*__first < __pivot)
	    ++__first;
	  --__last;
	  while (__pivot < *__last)
	    --__last;
	  if (!(__first < __last))
	    return __first;
	  std::iter_swap(__first, __last);
	  ++__first;
	}
    }

最后走优化的插入排序
  template<typename _RandomAccessIterator>
    void
    __final_insertion_sort(_RandomAccessIterator __first,
			   _RandomAccessIterator __last)
    {
      if (__last - __first > int(_S_threshold))
	{
	  std::__insertion_sort(__first, __first + int(_S_threshold));
	  std::__unguarded_insertion_sort(__first + int(_S_threshold), __last);
	}
      else
	std::__insertion_sort(__first, __last);
    }

另外,你可以看一下我写的几个排序算法和STL里的 sort 的对比数据:


用例中绿色区域的快速排序:

void swap(int *array, int pos_i, int pos_j) {
    int tmp = array[pos_i];
    array[pos_i] = array[pos_j];
    array[pos_j] = tmp;
}
int partition(int *array, int begin, int end);
// 尾递归版 这个可以减少栈空间的使用
void quick_sort_tail(int *array, int begin, int end) {
    while (begin < end) {
        int r = partition(array, begin, end);
        quick_sort_tail(array, begin, r - 1);
        begin = r + 1;
    }
}

int partition(int *array, int begin, int end) {
    // 在 begin 到 end 间随机找一个数 当 划分用的 key
    int rand_location = rand() % (end - begin + 1) + begin;
    int key = array[rand_location];
    // 把这个 key 移到 end 的位置,方便做双下标前移动
    swap(array, rand_location, end);
    int i = begin - 1;
    for (int j = begin; j < end; ++j) {
        if (array[j] <= key) {
            i++;
            swap(array, i, j);
        }
    }
    swap(array, i + 1, end);
    return i + 1;
}