0、学完《数据结构》课程后,了解到排序的多种算法,例如堆排序、快速排序、直接排序、归并排序等。同样我们使用c++标准库中sort()可以避免我们直接动手写一个排序的算法从而简单的达到我们所需要的效果。
思考一个问题:sort()使用的是哪种排序算法?
1、sort()实现的详解
1.1函数声明
#include <algorithm>
template< class RandomIt >
void sort( RandomIt first, RandomIt last );
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
stl提供两种调用sort()的方式,一是使用默认的 < 操作符比较,二是自定义比较函数方式。
bool operator < (const class &m)const {
return first < m.first;
}//运算符重载
bool less_second(const class & m1, const class & m2) {
return m1.second < m2.second;
}
1.2算法实现原理
STL中的sort不是普通的快速排序,更是对普通的快速排序进行优化,它还结合了插入排序和堆排序。根据不同的数量级别以及不同情况,能自动选用合适的排序方法。当数据量较大时采用快速排序,分段递归。一旦分段后的数据量小于某个阀值,为避免递归调用带来过大的额外负荷,便会改用插入排序。而如果递归层次过深,有出现最坏情况的倾向,还会改用堆排序。
普通的快速排序,每次选定第一个元素为基准元素将排序码分为前后两个子块,循环下去,直到排序完成。简单来说就是从中间到两端的排序。一般情况下时间复杂度是O(nlbn),但基准元素的选择不当即导致分割的子块不当,时间复杂度会恶化到O(n*n)。
1.3内省式排序 introsort
为了避免这种情况,David R.Musser于1996年提出一种混合式排序算法:Introspective Sorting(内省式排序),简称IntroSort,其行为大部分与上面所说的median-of-three Quick Sort完全相同,但是当分割行为有恶化为二次方的倾向时,能够自我侦测,转而改用堆排序,使效率维持在堆排序的 O(nlgn),又比一开始就使用堆排序来得好。
2、代码展示
下面是完整的SGI STL sort()源码(使用默认<操作符版)
template <class _RandomAccessIter>
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_loop便是上面介绍的内省式排序,其第三个参数中所调用的函数__lg()便是用来控制分割恶化情况,代码如下:
template <class Size>
inline Size __lg(Size n) {
Size k;
for (k = 0; n > 1; n >>= 1) ++k;
return k;
}
即求lg(n)(取下整),意味着快速排序的递归调用最多 2*lg(n) 层。
内省式排序算法如下:
template <class _RandomAccessIter, class _Tp, class _Size>
void __introsort_loop(_RandomAccessIter __first,
_RandomAccessIter __last, _Tp*,
_Size __depth_limit)
{
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))));
__introsort_loop(__cut, __last, (_Tp*) 0, __depth_limit);
__last = __cut;
}
}
- ######对代码还是不太理解,虽然理解sort()使用的算法。#########
- 3、所有sort算法和sort中的比较函数介绍
- 3.1 sort算法
- 1)sort()
- 2)stable_sort()//stable_sort的稳定性比sort强
- 3)partial_sort()
- 4)partial_sort_copy()
- 5)nth_element()//找出给定区间的某个位置对应的元素
- 6)is_sorted()
- 7)partition()
- 8)stable_partition()
- 3.2 比较函数
- equal_to
- not_equal_to
- less
- less_equal等
- 4、sort和容器
- 标准模版库的标准容器主要有vector,list,deque,map,set,string,stack,queue,
- multimap等。其中map,multimap,multiset是以树形结构存储数据,所以sort不可用。
述sort函数对于下列容器是可用的:
- vector
- string
- deque
- 5、合适的排序函数
- 如果数据很小,可以不考虑程序运行的效率,但当数据非常
- 大。
- 这是就要考虑到程序执行的效率,选择合理的排序函数会节省大
- 量的时间。
- 从效率上看,以下几种sort算法的是一个排序,效率由高到低(耗时由小变大):
- partion
- stable_partition
- nth_element
- partial_sort
- sort
- stable_sort
《Effective STL》对如何选择排序函数的总结摘取了一条:
若需对vector, string, deque, 或 array容器进行全排序,你可选择sort或stable_sort;
参考文献
1、《Effective STL》中文版
2、《STL源码剖析》 作者:侯捷
3、http://blog.youkuaiyun.com/bz201/article/details/543001
4、https://www.cnblogs.com/fengcc/p/5256337.html