直接贴第一段代码:
template<typename _RandomAccessIterator>
inline void
sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;
// concept requirements
__glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
_RandomAccessIterator>)
__glibcxx_function_requires(_LessThanComparableConcept<_ValueType>)
__glibcxx_requires_valid_range(__first, __last);
if (__first != __last)
{
std::__introsort_loop(__first, __last,std::__lg(__last – __first) * 2);
std::__final_insertion_sort(__first, __last);
}
}
:sort函数的前后范围区间定义的是两个迭代器,可以随机访问,但是不可以访问链表。这里首先讲一下没有自定义cmp函数的版本。排序的条件是first != last,也就是非空。
STL的排序是分为两个部分的,一个是优化的快排,另外一个是插入排序。可以理解,当排序数目少到一定数目,是插排的效率比较高的。这里采用(也是gcc采用的版本)都是选择前中后三个元素的中值来作为key值的,而且这里的优化快排函数还有第三个参数是lg(last - first)*2,主要是用来限制递归次数的,因为太深的递归需要很大的时间和空间开销,所以这里就限制快排递归到只剩一两个元素。
这里看看优化快排代码:
template<typename _RandomAccessIterator, typename _Size>
void
__introsort_loop(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Size __depth_limit)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType;
while (__last – __first > int(_S_threshold))
{
if (__depth_limit == 0)
{
_GLIBCXX_STD_P::partial_sort(__first, __last, __last);
return;
}
–__depth_limit;
_RandomAccessIterator __cut =
std::__unguarded_partition(__first, __last, _ValueType(std::__median(*__first,*(__first+ (__last- __first)/ 2),*(__last – 1))));
std::__introsort_loop(__cut, __last, __depth_limit);
__last = __cut;
}
}
发现刚才所说的那个递归次数被定义为__depth_limit,当递归太深(==0)且区间大小大于16的时候就不递归了,这个时候它就调用一个叫partial_sort的函数,这个也是stl库的一个内置函数,实现的功能就是部分排序,这里刚好可以用上。既然都看到这里了,我们不妨就把这个部分排序函数的源码也看看了。
部分排序:
template<typename _RandomAccessIterator>
inline void
partial_sort(_RandomAccessIterator __first,
_RandomAccessIterator __middle,
_RandomAccessIterator __last)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;
// concept requirements
__glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
_RandomAccessIterator>)
__glibcxx_function_requires(_LessThanComparableConcept<_ValueType>)
__glibcxx_requires_valid_range(__first, __middle);
__glibcxx_requires_valid_range(__middle, __last);
std::__heap_select(__first, __middle, __last);
std::sort_heap(__first, __middle);
}
这里采用的是堆排序,实现方式我基本看了一下,其实就是让前半部分形成一个最大堆,然后用for跟后面的一个一个比较,如果比堆根部最大的要小就交换两者位置(也就是扔掉大的),然后保持最大堆,然后扫描一次之后堆元素肯定比外面的小了,然后直接按照堆排序的算法,就能排出来了。
但是这里我觉得很诡异,因为他写的局部排序的三个参数居然是,first,last,last,也就说中间的定位参数居然也是last,也就是说这个根本就是全排,为什么要用局排呢,难道就是为了里面的堆排序么,如果是这样为什么不直接调用堆排呢?
这里的插入排序起的名字叫做__final_insertion_sort大致可以猜到,肯定是最后才用的插入排序,因为原队列越是整齐,插排的效率越高(希尔排序用的就是这个原理)。
最后的插排:
template<typename _RandomAccessIterator>
void
__final_insertion_sort(_RandomAccessIterator __first,
_RandomAccessIterator __last)
{
if (__last – __first > int(_S_threshold))
{
std::__insertion_sort(__first, __first + int(_S_threshold));
std::__unguarded_insertion_sort(__first + int(_S_threshold), __last);
}
else
std::__insertion_sort(__first, __last);
}
之前的优化快排有个定义是在enum
{ _S_threshold = 16 },这个16就是区间幅度,当区间小于16时就用插排。而且这里其实是有两个插排的,一个是unguarded的,一个是final的如果最后的数小于等于16个,直接就final了,如果超过十六个,那就前十六个final后面的unguarded。两者的区别就是final是正规插排,而unguarded就是把数插到前面的已经有序的区间内
原来虽然Quick和Heap的时间复杂性是一样的,但堆排序的常熟因子还是大些的,并且堆排序过程中重组堆其实也不是个省时的事。