STL源码解释-----sort()&&partial_sort()

直接贴第一段代码:

template
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
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
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
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的时间复杂性是一样的,但堆排序的常熟因子还是大些的,并且堆排序过程中重组堆其实也不是个省时的事。

你可能感兴趣的:(算法优化和STL)