排序
重要性……不用我说了。我们需要能够自己熟练地实现所有的基本算法(手写程度),尤其注意排序过程中是否取等的问题,while循环退出条件等细节。
综合的测评题:915
对了,我们先来观摩一下STL容器中的std::sort()的底层是怎么实现的吧。
原理篇
- 首先,关系型容器(底层为RB-tree)拥有自动排序功能,不需要调用sort,序列型容器中的stack,queue等有特定出入口,不允许排序,因此一般sort()针对的是vector和deque(list是双向迭代器,sort要求随机迭代器,因此list定义了相应的成员函数进行排序)
- 数据量大时采用QuickSort,分段归并排序。一旦分段后的数据量小于某个门槛(16),为避免QuickSort的递归调用带来过大的额外负荷,就改用插入排序。如果递归层次过深(2logn)还会改用HeapSort堆排序。
- 快排在最坏的情况下(在分隔时出现子区间为空的情况)时间复杂度达到O(n2),此时递归的层数也会越来越深。在数据量小的情况下,数据基本有序时,采用直接插入排序比快排更快。
- 堆排的时间复杂度恒为O(nlogn),无论数据有什么特征它的时间复杂度是不变的,虽然时间复杂度和快排相同,当时总体来说没有快排的速度快,主要原因在于,堆排涉及到大量的交换操作。
template <class RandomAccessIterator>
inline void sort(RandomAccessIterator first, RandomAccessIterator last) {
if (first != last) {
//快排+插入排序,递归深度限制是2^k<=size*2,最后一个参数是深度限制,即k
//该函数只会进行局部排序,即分段后的长度一旦小于16就不再处理,送给下一个函数
__introsort_loop(first, last, value_type(first), __lg(last - first) * 2);
//快排结束后,元素基本有序,依次用插入排序处理每个长度为16的段
__final_insertion_sort(first, last);
}
}
//_lg函数:计算递归深度,用来控制分割恶化,当递归深度达到该值就改用堆排序
//找出2^k <=n 的最大k,如n=20得k=4
template<typename _Size>
inline _Size __lg(_Size __n){
_Size __k;
for (__k = 0; __n != 1; __n >>= 1) ++__k;
return __k;
}
//涉及快排和堆排(注意只适合随机迭代器)
template<typename _RandomAccessIterator, typename _Size>
void __introsort_loop(_RandomAccessIterator __first,
_RandomAccessIterator __last,T*,
_Size __depth_limit){
//_S_threshold=16,每个区间必须大于16才递归
while (__last - __first > int(_S_threshold)){
//达到指定递归深度,改用堆排序,注意并不是从头开始堆排序,而是对当前排序的剩余部分用堆排序
if (__depth_limit == 0){
std::partial_sort(__first, __last, __last);//改用heapsort
return;
}
--__depth_limit;
//以下是partition,选择一个够好的枢轴并决定分割点
//media是三点中值决定函数,即决定哪个是中间值
_RandomAccessIterator __cut = __unguarded_partition
(__first, __last,T(std::__median(*__first,
*(__first+ (__last- __first)/ 2),
*(__last- 1))));
//右半段递归进行sort
std::__introsort_loop(__cut, __last, __depth_limit);
__last = __cut;
//通过最后一行实现区间缩短,开始对左半段进行sort(这种写法可读性较差)
}
}
template <class RandomAccessIterator, class T>
RandomAccessIterator __unguarded_partition(RandomAccessIterator first,
RandomAccessIterator last,
T pivot) {
while (true) {
while (*first < pivot) ++first;
--last;
while (pivot < *last) --last;
if (!(first < last)) return first;//交错,结束循环
iter_swap(first, last);//统一交换依次
++first;
}
}
//长度小于16时的插入排序
template <class RandomAccessIterator>
void __final_insertion_sort(RandomAccessIterator first,
RandomAccessIterator last) {
if (last - first > __stl_threshold) { //大于16
__insertion_sort(first, first + __stl_threshold);//将前16个元素进行插入排序