STL源码剖析 - sort() , __final_insertion_sort 解析

  • 只接受RandomAccess Iterator , listslist使用自己提供的member functions sort()

STL - Sort():

  • 数据量大时采用Quick Sort 分段递归排序;
  • 分段后的数据量小于某个门槛,为避免递归调用的额外负荷,改用Insertion Sort;
  • 递归层次过深,改用Heap Sort

实现如下:

template <class RandomAccessIterator>
inline void sort(RandomAccessIterator first, RandomAccessIterator last) {
    if (first != last) {
        __introsort_loop(first, last, value_type(first), __lg(last - first) * 2);
        __final_insertion_sort(first, last);
    }
}

先进行内省式快速排序introsort):

template <class RandomAccessIterator, class T, class Size>
void __introsort_loop(RandomAccessIterator first,
                      RandomAccessIterator last, T*,
                      Size depth_limit) {
    while (last - first > __stl_threshold) { // > 16
        if (depth_limit == 0) {
            partial_sort(first, last, last);	//递归过深,采用堆排序
            return;
        }
        --depth_limit;
        RandomAccessIterator cut = __unguarded_partition
          (first, last, T(__median(*first, *(first + (last - first)/2),
                                   *(last - 1))));
        __introsort_loop(cut, last, value_type(first), depth_limit);
        last = cut;	// 对左半端sort,这样写减少递归次数
    }
}

之后对若干个具有不同排序程度的子序列进行插入排序__lg() 控制分割恶化的情况。

template <class RandomAccessIterator>
void __final_insertion_sort(RandomAccessIterator first, 
                            RandomAccessIterator last) {
    if (last - first > __stl_threshold) {
        __insertion_sort(first, first + __stl_threshold);
        __unguarded_insertion_sort(first + __stl_threshold, last);
    }
    else
        __insertion_sort(first, last);
}

最后的插入排序,先判断是否大于阈值,若小于则直接进行一次插入排序,省时省力。
若大于阈值,则分为两部分:

  • 普通插入排序__insertion_sort
  • 不需要边界检查的插入排序__unguarded_insertion_sort

区别:
__insert_sort:

它会先将该值和第一个元素进行比较,如果比第一个元素还小,那么就直接将前面已经排列好的数据整体向后移动一位,然后将该元素放在起始位置。对于这种情况,和标准插入排序相比,它将last - first - 1次的比较与交换操作变成了一次copy_backward操作,节省了每次移动前的比较操作

如果该元素并不小于第一个元素,它会调用另外一个函数__unguarded_linear_insert,这里仅仅挨个判断是否需要调换,找到位置之后就将其插入到适当位置,而无需检测是否到达边界。

这里是因为if处不执行已经明确了,最小值在左侧,故不到边界便可停止。

template <class RandomAccessIterator, class T>
void __unguarded_linear_insert(RandomAccessIterator last, T value) {
    RandomAccessIterator next = last;
    --next;
    while (value < *next) {
        *last = *next;
        last = next;
        --next;
    }
    *last = value;
}

template <class RandomAccessIterator, class T>
inline void __linear_insert(RandomAccessIterator first, 
                            RandomAccessIterator last, T*) {
    T value = *last;
    if (value < *first) {
        copy_backward(first, last, last + 1);
        *first = value;
    }
    else
        __unguarded_linear_insert(last, value);
}

template <class RandomAccessIterator>
void __insertion_sort(RandomAccessIterator first, RandomAccessIterator last) {
    if (first == last) return; 
    for (RandomAccessIterator i = first + 1; i != last; ++i)
        __linear_insert(first, i, value_type(first));
}

__unguarded_insertion_sort :

template <class RandomAccessIterator, class T>
void __unguarded_insertion_sort_aux(RandomAccessIterator first, 
                                    RandomAccessIterator last, T*) {
    for (RandomAccessIterator i = first; i != last; ++i)
        __unguarded_linear_insert(i, T(*i));
}

template <class RandomAccessIterator>
inline void __unguarded_insertion_sort(RandomAccessIterator first, 
                                RandomAccessIterator last) {
    __unguarded_insertion_sort_aux(first, last, value_type(first));
}

使用此函数时,一定得确保这个区间的左边有效范围内已经有了最小值,否则不检查边界的后果很严重。

将经过__introsort_loop排序的数据分成两段,假设第一段里面包含了最小值,那么将第一段使用__insertion_sort排序,后一段使用__unguarded_insertion_sort便可以达到效率的最大化.

这就是__final_insertion_sort 所采用的:

template <class RandomAccessIterator>
void __final_insertion_sort(RandomAccessIterator first, 
                            RandomAccessIterator last) {
    if (last - first > __stl_threshold) {
    // 快排性质决定了左边元素比右边区间元素小,可以证明最小元素在前16个元素内
        __insertion_sort(first, first + __stl_threshold);
    // 确定了最小值在左边后,便可调用无边界检查的快速排序
        __unguarded_insertion_sort(first + __stl_threshold, last);
    }
    else
        __insertion_sort(first, last);
}

快排性质决定了左边元素比右边区间元素小,可以证明最小元素在前16个元素内。 确定了最小值在左边后,便可调用无边界检查的快速排序。

Reference

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值