STL 中sort源码分析

以SGI的STL为例

 

sort有两种重载形式

 

template <class RandomAccessIterator>

void sort(RandomAccessIterator first, RandomAccessIterator last);

template <class RandomAccessIterator, class StrictWeakOrdering>
void sort(RandomAccessIterator first, RandomAccessIterator last,
          StrictWeakOrdering comp);
这两种形式都要求形随机访问迭代器,因此只能用于容器vector或deque,这里我们只以第一种为例进行讲解
在数据量大时,采用Quick Sort,分段递归排序,一旦分段后的数据量小于门限值时,为避免递归的额外开销,便采用Insertion sort。
如果递归的层次过深,还会使用Heap Sort.
对于以下的以__开头的命名函数,表示其为被内部的其它函数调用,而不能被用户直接调用。
首先来看插入排序
template <class _RandomAccessIter>
void __insertion_sort(_RandomAccessIter __first, _RandomAccessIter __last) {
  if (__first == __last) return; 
  for (_RandomAccessIter __i = __first + 1; __i != __last; ++__i)
    __linear_insert(__first, __i, __VALUE_TYPE(__first));
}
其所调用的 __linear_insert如下示:
template <class _RandomAccessIter, class _Tp>
inline void __linear_insert(_RandomAccessIter __first, 
                            _RandomAccessIter __last, _Tp*) {
  _Tp __val = *__last;
  if (__val < *__first) {
    copy_backward(__first, __last, __last + 1);
    *__first = __val;
  }
  else
    __unguarded_linear_insert(__last, __val);
}
这里需要对其进行一下说明,通常情况下在进行插入排序时,既要进行大小的比较,又要对边界进行控制,经过上面的改进后,但只需要进
行大小的比较便可,这就是代码的高明这处。
首先对要插入有序部分的元素__val与队首的最小元素 *__first进行比较,如果__val < *__first,则只__first与 __last之间的元素向后移一个
位置,然后将__val插入队首。
如果__val >= *__first,则说明__val在将要新生成的有序队列中不是最小,因此,在下面的while中不用进行界限控制,只比较元素的大小
即可。
template <class _RandomAccessIter, class _Tp>
void __unguarded_linear_insert(_RandomAccessIter __last, _Tp __val) {
  _RandomAccessIter __next = __last;
  --__next;
  while (__val < *__next) {
    *__last = *__next;
    __last = __next;
    --__next;
  }
  *__last = __val;
}
在STL中对避免快排时每次都选择最小或最大的元素做轴,使用以下函数选择一个三点中值。
template <class _Tp>
inline const _Tp& __median(const _Tp& __a, const _Tp& __b, const _Tp& __c) {
  __STL_REQUIRES(_Tp, _LessThanComparable);
  if (__a < __b)
    if (__b < __c)
      return __b;
    else if (__a < __c)
      return __c;
    else
      return __a;
  else if (__a < __c)
    return __a;
  else if (__b < __c)
    return __c;
  else
    return __b;
}
下面是快排中所要使用的分割函数。对[first,last)区间的元素进行分割,使用得中轴右边的元素大于等于中轴,左边的元素小于等于中轴,
并返回中轴所在位置。
template <class _RandomAccessIter, class _Tp>
_RandomAccessIter __unguarded_partition(_RandomAccessIter __first, 
                                        _RandomAccessIter __last, 
                                        _Tp __pivot) //__pivot为中轴
{ while (true) { while (*__first < __pivot) ++__first; --__last; while (__pivot < *__last) --__last; if (!(__first < __last)) return __first; iter_swap(__first, __last); ++__first; } }
sort 代码如下
template <class _RandomAccessIter>
inline void sort(_RandomAccessIter __first, _RandomAccessIter __last) {
  
        if (__first != __last) {
    __introsort_loop(__first, __last,
                     __VALUE_TYPE(__first),
                     __lg(__last - __first) * 2);
    __final_insertion_sort(__first, __last);
  }
}
基中__lg(n)用来找出2^k<=n的最大k值,用以控控制递归的深度。
template <class _Size>
inline _Size __lg(_Size __n) {
  _Size __k;
  for (__k = 0; __n != 1; __n >>= 1) ++__k;
  return __k;
}
下面的是用来对区间使用快排,以达到部分有序状态。
template <class _RandomAccessIter, class _Tp, class _Size>
void __introsort_loop(_RandomAccessIter __first,
                      _RandomAccessIter __last, _Tp*,
                      _Size __depth_limit)
{
  while (__last - __first > __stl_threshold) {//__stl_threshold在这里用前面定义的常量16,当区间多于16个元素时,才有必要使用快排。
if (__depth_limit == 0) {//当递归的层次过深时,使用堆排序。 partial_sort(__first, __last, __last); return; } --__depth_limit; _RandomAccessIter __cut = __unguarded_partition(__first, __last, _Tp(__median(*__first, *(__first + (__last - __first)/2), *(__last - 1))));//使用分割函数,反回中轴 __introsort_loop(__cut, __last, (_Tp*) 0, __depth_limit);//对右半部分进行递归排序 __last = __cut;//使尾指针指向中轴,即要对左半部分排序 } }
最后使用插入排序对各部分进行排序。
template <class _RandomAccessIter>
void __final_insertion_sort(_RandomAccessIter __first, 
                            _RandomAccessIter __last) {
  if (__last - __first > __stl_threshold) {
    __insertion_sort(__first, __first + __stl_threshold);
    __unguarded_insertion_sort(__first + __stl_threshold, __last);
  }
  else
    __insertion_sort(__first, __last);
}
些函数先判断元素个数是否大于16,如是大于,则先用__insertion_sort()对16个元素的子序列排序,再用__unguarded_insertion_sort()对
其余的排序。否则直接用__insertion_sort()排序。
template <class _RandomAccessIter>
inline void __unguarded_insertion_sort(_RandomAccessIter __first, 
                                _RandomAccessIter __last) {
  __unguarded_insertion_sort_aux(__first, __last, __VALUE_TYPE(__first));
}

template <class _RandomAccessIter, class _Tp, class _Compare>
void __unguarded_insertion_sort_aux(_RandomAccessIter __first, 
                                    _RandomAccessIter __last,
                                    _Tp*, _Compare __comp) {
  for (_RandomAccessIter __i = __first; __i != __last; ++__i)
    __unguarded_linear_insert(__i, _Tp(*__i), __comp);
}
template <class _RandomAccessIter, class _Tp>
void __unguarded_linear_insert(_RandomAccessIter __last, _Tp __val) {
  _RandomAccessIter __next = __last;
  --__next;
  while (__val < *__next) {
    *__last = *__next;
    __last = __next;
    --__next;
  }
  *__last = __val;
}
ps:个人感觉__final_insertion_sort()中区分区间大小是多此一举,因为最终其调用的都是__unguarded_linear_insert(),其并没用针对不
同的大小区间采用明显不用的算法。

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值