stl中sort分析

stl中最复杂庞大的算法无疑是sort,我当时在看这个算法时也费了不少时间,今天来总结一下,正好也复习一下部分排序算法.

  1. 该算法接受只两个RandomAccessIterators,然后将区间的所有元素以某种排序标准来排序(默认是递增排序).
    值得注意的是,stl中所有关系型容器都有自动排序功能(标准规定是map,set,multiset,multimap),所以也用不到sort.对于序列器stack,queue,priority-queue,这些都有特定的出入口,不允许用户对元素排序.
    那么我们也只剩下了vector,deque,list,前两者的迭代器是RandomAccessIterators,适合使用sort算法,list的迭代器属于BidiectioinalIterators,不在STL标准之列的slist,其迭代器更属于ForwardIterators,都不适合用sort算法,如果要对list和slist排序,应该使用他们的成员函数sort算法.

  2. 先说大体思路,sort算法在数据量大时采用Quick Sort,分段递归排序.一旦分段后的数据量小于某个门槛,为了避免Quick Sort的递归调用带来过大的额外符合,就改用Insertion Sort.如果递归层次过深,我们还会调用Heap Sort.

  3. 先说一下插入排序,先贴一下代码吧,然后我再根据书上的图片来进行解释.


    image.png

    image.png

    值得注意一下,这个函数在调用的过程中,在last前面的序列都是已经排序好的,这是我们写这个函数的基础,如果我们不知道这个情况,那么我们理解起来就会十分的费劲.先看第一种情况,如果value的值小于first的值,那么我们只需要将value放到第一位就OK了(因为first肯定是已经排序序列最小的值),值得一提的是我们使用的是copy_backward,具体原因大家可以自己想想.另外一种情况我们调用了__unguarded_linear_insert,也就是下面这个函数.

    image.png

    这个函数实际上很好理解,因为我们只需要找到value应该被存放的合适的位置就可以了,因此我们只需要不断的左移last迭代器,直到找到一个值不大于value,然后在value放在该值后面的位置就可以了.


    image.png

    这个图通过我上面的分析之后应该很好理解了,只是通过几个函数调用显得稍微复杂一点.

  4. Quick Sort
    因为插入排序的复杂度是O(N^2),所以面对大数据量,效率就不敢恭维了,在这里我不在叙述快排的原理了,因为我前面有篇博客专门讲解了快排的原理,还不熟悉的同学可以去看看那篇博客.

    • 下面说一下Median-of-Three,这个是用来选择快排枢轴的算法,思路是选择整个序列头,尾,中央三个位置的元素,以其中值作为枢轴,这种做法就叫三点中值快排算法.为了快速取出中央位置的元素,显然迭代器必须能够随机定位,也就是说必须是个RandomAccessIterators.


      image.png

      代码就不上了,很简单的比较算法.

    • 分割
      我们定完了枢轴后,下面要做的就是把元素分为小于枢轴的和大于枢轴的,针对这个问题,下面叙述一个简单而又十分有成效的做法.令头端迭代器向尾部移动,尾端迭代器last向头部移动.当*first大于或者等于枢轴的时候就停下来,当*last小于或等于枢轴的时候也停下来,(个人认为两种情况下迭代器指向值等于枢轴的时候不停下来也是没有问题的)然后检验两个迭代器是否交错,如果未交错就将两个元素互换.如果交错了,说明序列已经调整完毕.此时,以first为轴,将序列分为左右两半,左边的元素都小于等于枢轴,右边的元素都大于等于枢轴.



      image.png

      image.png

      我相信结合例子来看,这个算法也是不难理解的.

    • 阈值(threshold)
      从效率的角度来看,当元素数量很少的时候(例如只有十来个),使用快排这样比较复杂的算法是不太划算的,小数据量的情况下,插入排序也可能会快与快排,因为快排会产生的很多的函数递归调用.
    • final insertion sort
      对于几近排序但是尚未完成的序列,插入排序的表现非常好,所以我们在快排到了序列比较短的时候,改用插入排序而不是彻底排好序(从效率的角度考虑).
    • introsort
      不当的枢轴选择,导致不当的分割,导致快排恶化为O(N^2).而introsort则没这个问题,当分割没有问题时,它的表现和三点中值快排完全相同,但是当分割行为有恶化为二次行为的倾向时,能够自我侦测,转而使用Heap Sort,使效率维持在O(NLogN),又比一开始就使用Heap Sort来的好.
  5. sort
    下面是sort算法的源代码.


    image.png

    关于__lg(),是用来控制分割恶化的情况.当元素个数为40时,__introsort_loop的最后一个参数是5*2,也就是10,代表最多分割四层.


    image.png

    函数一开始就判断序列的大小,__stl_threshold默认是常数16,如果序列大小小于16,我们就等着调用插入排序.当大于16时,我们再检查分割层次,如果分割层次超过了指定值(例子中是10),说明我们的分割趋向于恶化,因为分割层次过深,这个指定值的选择是有一定含义在内的,超过了分割层次我们就调用partial sort,也就是堆排序.
    如果这些都没问题,我们就开始进入快排的阶段了,找出分割点,然后对左右段落递归进行IntroSort.当introsort结束之后,显然我们得到的是很多"元素个数少于16"的子序列,每个子序列都有相当程度的排序,但尚未完全排序.然后我们再回到主函数,下一步调用__final_insertion_sort()
    image.png

    这个函数先判断元素个数是否大于16,如果答案为否,我们就调用__insertion_sort来进行处理.如果答案是肯定的,就将[first,last)分割为长度为16的子序列,和另一段剩余子序列,再针对两个子序列分别调用__insertion_sort()和__unguarded_insertion_sort().前者的代码前面已经有了,下面展示后面的代码:


    image.png

    之所以我们在最后没有调用quicksort的原因是,到了这一步,整个序列已经基本被排列好了,也就是排序程度很高,但没有完全排序,所以在这里我们只用了插入排序,这样的效率是高于快排的.到这里,sort算法就讲的差不多了.

结语:写到这里,sort算法也算是说的差不多了,确实是一个相当复杂的一个算法,用到了很多的优化技巧以及为了效率"无所不用其极",当然为了效率,这些都是值得.排序算法是我们生活中非常常用的一个算法,我们要做到熟悉常见的排序算法,还要对其复杂度以及应用场景做到很熟悉,这样在实际code的过程中就可以做到游刃有余的应用了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容