STL的sort函数浅析
用了这么久的sort,大概知道它是快排加一些其他排序组合起来的一个排序算法,今天就来捋清楚。
直接上图片:
sort定义在stl_algo.h里,看起来只不过是对另一个函数的封装。
__sort函数也定义在stl_algo.h里:
可以看到它首先判断左右迭代器是否指代有效区间,如果不是有效区间就直接返回。
如果有效:先调用一个__introsort_loop算法,再调用__final_insertion_sort算法。
其中的_S_threshold是一个枚举值,等于16。
也就是当区间长度大于16的时候,才会进入循环。否则直接返回之前的__sort函数里执行__final_insertion_sort算法。
循环内部:每次__depth_limit先减1,然后:
_RandomAccessIterator __cut =
std::__unguarded_partition_pivot(__first, __last, __comp);
unguarded_partition_pivot这个名字显然是个快排的划分算法,返回一个随机迭代器__cut代表切分点,我们点进去看看:
可以看到其把区间中点和起始点进行了交换,应该是为了随机化处理,防止快速排序退化为n^2。之后调用另一个划分函数__unguarded_partition:
嗯。。看起来就是个划分函数,没有再深入的必要了。那么我们回到__introsort_loop函数:
黄线这行将区间first到last做了一次划分,划分点为cut。然后调用了自己:调用的参数区间为cut到last,也就是右半部。注意这里是尾递归。
递归过程先不考虑,假设执行完这次递归调用后,即我们对cut到last执行完了一次__introsort_loop函数。
然后将__last的值赋为__cut,这样下次循环我们对左区间继续划分。
继续循环,__depth_limit继续减1,显然经过有限次循环,最终__depth_limit会等于0。
这时执行if语句:
__partial_sort函数:
看起来就是一个堆排序算法,没什么特别的,就不深入看了。
回到之前的if语句,调用完这个堆排序算法后,我们的__introsort_loop函数就退出了。
这样我们又回到了__sort函数:
下面的__final_insertion_sort显然是个插入排序算法,我们也不用深入看了。
至此,整个__sort函数完毕,也就是我们的排序算法到此结束了。
总结一下:
STL的sort函数会先调用一个__introsort_loop算法,其实这个算法有名字,叫做内省排序:https://baike.baidu.com/item/%E5%86%85%E7%9C%81%E6%8E%92%E5%BA%8F/6471937?fr=aladdin
这个排序算法首先从快速排序开始,当递归深度超过一定深度(深度为排序元素数量的对数值)后转为堆排序。
和我们观察结果是一样的:当区间长度大于16的时候,进行递归快排。
如果达到递归层数限制,改为调用堆排序并返回。
如果区间长度小于16了,改为调用插入排序。
这样的好处是:
到了最后一步,整个序列已经基本被排列好了,也就是排序程度很高,但没有完全排序,所以在这里我们只用了插入排序,这样的效率更好。
说的不对可以评论指出,欢迎转载,标明出处哦~