c++ 中
sort
函数的具体实现在不同编译器下不同,本篇代码来自msvc编译器,版本号14.24.28314
函数原型
首先可以看到sort
函数有两个原型,如下,
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
第一个函数是将[first,last)
之间的元素按照升序排序,实质上是第二个函数的一个特例,即
comp=less<>()
的情况。这在代码中也可以得到验证,第一个函数中实际上直接对第二个函数进行了调用,代码如下:
template <class _RanIt>
void sort(const _RanIt _First, const _RanIt _Last) { // order [_First, _Last), using operator<
_STD sort(_First, _Last, less<>());
}
另外,需要说明的是,sort是一种不稳定排序,若要使用稳定排序,请使用函数stable_sort
.
不稳定排序: 对于值相等的元素,排序时不保证不改变其序列。举个简单的例子。新学期开始了,男女生要按照高矮个排队,小明和小亮
一样高,本来小明在小亮前边位置正好可以和班花同桌 ,可是按照高矮个排序之后,小明竟然和小亮的位置交换了。这就是不稳定排序。
sort代码
下边便是sort
函数的实现代码,先不执着于细节,可以看出sort
中使用了三种排序算法:快速排序,堆排序和插入排序。
template <class _RanIt, class _Pr>
void _Sort_unchecked(_RanIt _First, _RanIt _Last, _Iter_diff_t<_RanIt> _Ideal, _Pr _Pred) {
// order [_First, _Last), using _Pred
_Iter_diff_t<_RanIt> _Count;
while (_ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal) { // divide and conquer by quicksort
auto _Mid = _Partition_by_median_guess_unchecked(_First, _Last, _Pred);
// TRANSITION, VSO#433486
_Ideal = (_Ideal >> 1) + (_Ideal >> 2); // allow 1.5 log2(N) divisions
// 约等于 1.26不是1.5,难道是我错了?
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;
}
}
if (_ISORT_MAX < _Count) { // heap sort if too many divisions
_Make_heap_unchecked(_First, _Last, _Pred);
_Sort_heap_unchecked(_First, _Last, _Pred);
} else if (2 <= _Count) {
_Insertion_sort_unchecked(_First, _Last, _Pred); // small
}
}
快速排序阶段
尽管sort最终的排序不是快速排序完成的,但是排序开始阶段首先进行的是快速排序。即上边代码中while
循环部分,也可以看出此处的快速排序
是通过递归实现的。
快速排序:通过一趟排序将要排序的数据分割成独立的两部分,
其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序
快速排序的平均时间复杂度为
,可是在最坏情况下会是
,这就是复杂度恶化。怎么防止复杂度恶化呢?
可以在某种触发条件下进入堆排序或插入排序过程。其中堆排序不是必然进入的。
堆排序阶段
正如上边说的,堆排序不是必然进入的一个过程,只有一种情况下会进入堆排序,即0 == _Ideal
,_Ideal
这个变量用于控制
递归深度,注释中说将递归深度控制在1.5 log2(N) ,我算了下是约等于1.26不是1.5,难道是我算错了?
插入排序
对于区间宽度小于等于_ISORT_MAX
时,进入插入排序阶段,这是排序的最后阶段。其中,
// COMMON SORT PARAMETERS
const int _ISORT_MAX = 32; // maximum size for insertion sort
插入排序适用于已经有部分数据已经排好,并且排好的部分越大越好,因此在排序的最后阶段选用了插入排序作为终结。