归并排序的STL源码浅析(富帅版)
在之前的归并排序C++实现中,已经给出了我自己写的代码。当然了,与STL版本所考虑的,以及所设计的完整性与安全性,仍然有不小差距。在这些源码中,inplace_merge适用于已序区间。将两个连接在一起的序列[first,middle)和[middle,last)都已排序,并仍然保持排序的特性。如果原先两个序列是递增的,执行结果也递增;如果两个序列递减,执行结果也递减。inplace_merge,merge_without_buffer和merge_adaptive都有两个版本——第一版本默认operator<,第二版本用指定的Functor(comp)进行比较。由于两个版本只有符号方面有区别,而其他绝大部分如出一辙。所以这里主要讨论operator<的版本。
首先看看主函数inplace_merge:
inplace_merge先判断两个之前已排序的序列(requires_sorted)是否为空,只要有任何一个序列为空,就什么都没必要再做下去。如果两个序列都不为空,就计算两序列的长度len1和len2,以及配置临时缓冲区Temporary_buffer;如果配置成功,则将两段序列及序列长度送入merge_adaptive中,也就是缓冲区足够大的情况;如果失败,就送入merge_without_buffer中,即使缓冲区不足,也能运作,就是效率不太高。
接着看看辅助函数merge_without_buffer(缓冲区空间不足时)的分配方案:
在缓冲区不足的情况下,就需要紧缩银根过日子。对两个序列的拆分也需要精打细算。取两个变量len11和len22作为对两个子序列再进行递归拆分后的长度。 两个子序列中,取更长的一段一分为二,而first_cut和second_cut为在不破坏序列顺序的情况下,可插入的最后一个合适的位置。在找到拆分位置后,仍需要调用rotate算法。最后就对子序列再次进行递归拆分,继续无缓冲区的一分为二,直至最后获得的两序列为空,或者两序列只有最后两个元素,就能比较大小并结束递归了。
然后看看辅助函数merge_adaptive(缓冲区足够的情况下)的分配方案:
在缓冲区足够的情况下,就应该充分利用空间,提高效率。对两个序列的拆分就可以大方一些。当有缓冲区的时候,仍需要缓冲区的大小是否足以安置序列。如果缓冲区足够安置某一段序列,直接利用merge或merge_adaptive来复制到缓冲区中,再将两区间合并,形成一个有序的区间;如果确实有缓冲区空间,但不足以安置任何一个序列,就需要调用rotate_adaptive版本,来对递归拆分之后原长度一半的序列进行尝试,看看更小的序列能否容纳于缓冲区中。
接着看看辅助函数rotate_adaptive:
rotate_adaptive与rotate基本没有什么不同,都用来处理两序列间元素互换。只是rotate_adaptive有了缓冲区的襄助,可以利用move3以及move_backward3移动到一小段缓冲区中,但如果缓冲区依然不足,就只能调用rotate算法,没有缓冲区的版本了。
参考归并排序的C++实现(大众版),可发现,STL版本的归并排序,在架构方面,按照各种不同的情况调用合理的STL函数,并利用Bidirectional Iterator作为默认迭代器;尤其针对缓冲区的空间大小不同情况,处理了好几种可能并实现出来。这么做就比我自己写的版本更加完善和安全。
归并排序要提高效率,就不可避免地需要消耗更多的空间和临时内存,而且也要花费将数据复制到缓冲区再复制回来等附加工作;尽管归并排序很容易理解,并且比较次数很少,但由于消耗空间大,很难用于主存排序。而在平常的排序工作中,主要应用快速排序解决问题。有关快速排序的问题,后续博文会详细说明。
本博文的写作目的是更好地掌握归并排序以及初涉与归并排序相关的STL源代码相关心得体会,若各位优快云er需要掌握更多,请参阅《STL源码剖析》。
参考文献及链接:
4.http://blog.youkuaiyun.com/mourinho_my_idol/article/details/12426467