『嗨威说』算法设计与分析 - STL中Sort函数的实现原理初探

本文索引目录:

1、对Sort算法实现的我的阅读体会html

2、Sort算法使用的三个排序算法的优势介绍算法

  2.1  插入排序的优缺点数组

  2.2  堆排序的优缺点数据结构

  2.3  快速排序的优缺点框架

  2.4  新的结合排序——内省式排序的出现dom

3、sort函数的具体实现函数

4、尾录oop

 

 

1、对Sort算法实现的我的阅读体会:

    

    如同《STL源码剖析》中所说,人类生活在一个有序的世界中,没有排序,不少事情没法进展,可是对于排序来讲,面对大数据的排序存在着效率的问题。咱们不可能说对十万个数进行冒泡排序,这在时间成本上是极不现实的。因此,STL为了追求效率极致,结合三大较为快速的排序:快排、堆排、插排来实现对大数据的排序。post

    STL是C++很是出色和有较高效率的代码框架,以优化获得更高效率为目标的大做品,更加的体现了C++的高效率。在STL中,有一个排序函数sort无不把这一目标体现得淋淋尽致。为了获得更快的排序效率,sort函数根据序列的大小,自动地合理使用快排(Quick Sort)、插排(Insertion Sort)和堆排(Heap Sort)。学习

    STL的开发者还很是有意思,经过多个方面来决定这三个排序的使用,例如当问题规模较少时,为了减小递归函数调用的资源占用,则采起插排来迅速对小额数据量完成排序,在数据量不大时,插排的速度效率彻底是能够超过快排的。当问题规模适中时,STL能够先进行快排,而后当快排递归分组的序列组达到必定小的问题规模时,同上实行插排,完成小额排序的任务。当问题规模过大时,STL开发者考虑到了快排调用递归层的深度,为了防止递归层过深过多,面对大额数据量时,会改用堆排。

    这或许就是算法工程师的魅力吧,追求高效的算法,也映射出了一些道理,在从此的工做学习生活中,一个高效的工做/生活/学习算法,能给你带来多大的效益,所以摸索更高效的方法尤其重要。

 

 

2、Sort算法使用的三个排序算法的优势介绍:

2.1  插入排序的优缺点:

 

    插入排序不适合对于数据量比较大的排序应用。可是,若是须要排序的数据量很小,例如,量级小于千;或者若已知输入元素大体上按照顺序排列,那么插入排序仍是一个不错的选择。插入排序最好时间复杂度为O(n)、最坏时间复杂度为O(n^2),空间复杂度为O(1),属于稳定排序。

 

 

2.2  堆排序的优缺点:

 

 

     堆排序常常是做为快速排序最有力的竞争者出现,它们的复杂度都是O(N logN)。可是,有一点它却比快速排序要好不少:最坏状况它的复杂度仍然会保持O(N logN),这一优势对本文介绍的新算法有着巨大的做用。

 

 

2.3  快速排序的优缺点:

 

 

    快速排序的优势无疑就是在内排序最快的排序了,时间复杂度能够达到O(NlogN),可是可能因为不当的pivot选择,致使其在最坏状况下复杂度恶化为O(N2)。另外,因为快速排序通常是用递归实现,咱们知道递归是一种函数调用,它会有一些额外的开销,好比返回指针、参数压栈、出栈等,在分段很小的状况下,过分的递归会带来过大的额外负荷,从而拉缓排序的速度。

 

 

2.4  新的结合排序——内省式排序的出现:

    因为快速排序有着前面所描述的问题,所以Musser在1996年发表了一遍论文,提出了Introspective Sorting(内省式排序)。它是一种混合式的排序算法,集成了前面提到的三种算法各自的优势:

      =》在数据量很大时采用正常的快速排序,此时效率为O(logN)。

      =》一旦分段后的数据量小于某个阈值,就改用插入排序,由于此时这个分段是基本有序的,这时效率可达O(N)。

      =》在递归过程当中,若是递归层次过深,分割行为有恶化倾向时,它可以自动侦测出来,使用堆排序来处理,在此状况下,使其效率维持在堆排序的O(N logN),但这又比一开始使用堆排序好。

    由此可知,它乃综合各家之长的算法。也正由于如此,C++的标准库就用其做为std::sort的标准实现。

 

 

3、sort函数的具体实现:

    注意:若要使用sort函数,须要包括头文件#include<algorithm>。

    在老师推荐的《STL源码剖析》给出了sort的函数原型:

template <class RandomAccessIterator> inline void sort(RandomAccessIterator first, RandomAccessIterator last) { if (first != last) { __introsort_loop(first, last, value_type(first), __lg(last - first) * 2); __final_insertion_sort(first, last); } }

    咱们能够发现,sort函数须要针对RandomAccessIterator,这是一个随机存取迭代器,通常针对数组、结构体数组、类数组、vector容器、deque使用,其余的容器通常有内置的sort排序算法。

    首先if判断序列区间的有效性,不是单个对象,接着调用__introsort_loop,在该函数结束以后,最后调用插入排序(__final_insertion_sort)。

    对于__introsort_loop函数STL展现以下:

template <class RandomAccessIterator, class T, class Size>
void __introsort_loop(RandomAccessIterator first,RandomAccessIterator last, T*,Size depth_limit) { while (last - first > __stl_threshold) { if (depth_limit == 0) { partial_sort(first, last, last); return; } --depth_limit; RandomAccessIterator cut = __unguarded_partition(first, last, T(__median(*first, *(first + (last - first)/2),*(last - 1)))); __introsort_loop(cut, last, value_type(first), depth_limit); last = cut; } }

    在这函数中:能够看出它是一个递归函数,由于咱们说过,Introspective Sort在数据量很大的时候采用的是正常的快速排序,所以除了处理恶化状况之外,它的结构应该和快速排序一致。除此以外,STL还使用了三点中值法来防止排序恶化。

    除了以上两个特色

      ·递归函数

      ·三点中值为标记

    STL还针对partition分割函数进行了优化,并在书中给出了很是简单明了的图解:

 

    最后STL还对递归深度进行了实时判断:

      ·递归深度

    __introsort_loop函数中的depth_limit是前面所提到的判断分割行为是否有恶化倾向的阈值,即容许递归的深度,调用者传递的值为2logN。注意看if语句,当递归次数超过阈值时,函数调用partial_sort,它即是堆排序,堆排序结束以后直接结束当前递归。

      ·最小分段阈值

    除了递归深度阈值之外,Introspective Sort还用到另一个阈值。注意看__introsort_loop中的while语句,其中有一个变量__stl_threshold,其定义为:const int __stl_threshold = 16;

    它就是咱们前面所说的最小分段阈值。当数据长度小于该阈值时,再使用递归来排序显然不划算,递归的开销相对来讲太大。而此时整个区间内部有多个元素个数少于16的子序列,每一个子序列都有至关程度的排序,但又还没有彻底排序,过多的递归调用是不可取的。而这种状况恰好插入排序最拿手,它的效率可以达到O(N)。所以这里停止快速排序,sort会接着调用外部的__final_insertion_sort,即插入排序来处理未排序彻底的子序列。

 

    对于__final_insertion_sort函数原型展现以下:

template <class RandomAccessIterator, class T>
void __unguarded_linear_insert(RandomAccessIterator last, T value) { RandomAccessIterator next = last; --next; while (value < *next) { *last = *next; last = next; --next; } *last = value; } template <class RandomAccessIterator, class T> inline void __linear_insert(RandomAccessIterator first, RandomAccessIterator last, T*) { T value = *last; if (value < *first) { copy_backward(first, last, last + 1); *first = value; } else __unguarded_linear_insert(last, value); } template <class RandomAccessIterator>
void __insertion_sort(RandomAccessIterator first, RandomAccessIterator last) { if (first == last) return; for (RandomAccessIterator i = first + 1; i != last; ++i) __linear_insert(first, i, value_type(first)); }

    STL的插入排序和咱们以前在数据结构课程中所学的插入排序还略有不一样,在STL的插入排序中用到了一些很妙的优化效率解决方式,但鉴于个人表达能力有限,为了避免误导他人对sort里插排的理解,就不在此赘述讲解。

 

 

4、尾录:

    很是感谢侯捷的《STL源码剖析》以及老师推荐的feihu做者,在脑海中或者笔记上总结,和汇聚成文相比彻底是两回事,有太多的背景要介绍,述说的方式,结构的安排等等无一不需花费心思。如今能够体会出每一个写做者的艰辛之处,更对认真完成众多经典做品的做者们充满了敬佩。确实如此,就连我在简单编写浅显的博客的时候,都得思考如何排版结构阐述、合理解释,这都须要花费很多心思和精力,但这些优秀做品无疑能给他人带来巨大帮助,为此以这些优秀的做者为目标,但愿可以有机会分享富有高质量高水平的知识点讲解给有须要的人。

 

 

 

若有不合理的地方,请及时指正,我愿听取改正~

原文出处:https://www.cnblogs.com/WinniyGD/p/11603777.html

相关文章
相关标签/搜索