深入解读并总结【排序】的解题技巧和注意事项

排序

重要性……不用我说了。我们需要能够自己熟练地实现所有的基本算法(手写程度),尤其注意排序过程中是否取等的问题,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个元素进行插入排序
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值