Sort
stl所提供的各式各样的算法中,sort()是最复杂庞大的一个。这个算法接受两个随机存取迭代器,然后将区间内的所有元素以渐增方式由小到大重新排列。还有个版本则允许用户指定一个仿函数,作为排序标准。
stl中的所有关系型容器都拥有自动排序功能,所以不需要sort算法。序列式容器中的stack,deque和priority_queue都有特别的入口,不允许用户对元素排序。剩下的vector,deque和list,前两者的迭代器属于RandomAccessIterators,适合用sort算法,list的迭代器则属于BidrectionalIterators,不适合用sort算法。
STL的sort算法,数据量大时采用Quick Sort(快速排序),分段递归排序,一旦分段后的数据量小于某个门槛,为避免Quick Sort的递归调用带来过大的额外负荷,就改用Insertion Sort(插入排序)。如果递归层次过深,还会改用Heap Sort(堆排序)。
STL中的Insertion Sort
默认版本(版本二允许指定一个仿函数作为俩元素的比较函数)
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)); //[first,i)形成一个子区间
}
//辅助函数
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, class T>
void __unguarded_linear_insert(RandomAccessIterator last, T value) {
RandomAccessIterator next = last;
--next;
//一旦不再出现逆序对,循环就可以结束了
while (value < *next) { //逆序对存在,需要进行调整
*last = *next;
last = next;
--next;
}
*last = value; //value的正确落脚处
}
STL中的Quick Sort
Quick Sort是目前已知最快的排序法,平均复杂度O(NlogN)。早期的STL sort算法都采用Quick Sort,SGI STL已改用IntroSort。
Median-of-Three(三点中值)
快速排序首先要选择枢纽,为了避免“元素当初输入时不够随机”所带来的恶化效应,理想的方法是取整个序列的头、尾、中央三个位置的元素,以其中值为枢纽。
template <class T>
inline const T& __median(const T& a, const T& b, const T& c) {
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;
}
Partitionining(分割)
分割方法有很多,以下叙述既简单又有良好成效的做法。令first向尾移动,last向头移动。当*first大于或等于pivot时停下来(应该往后放),当*last小于或等于pivot时也停下来(应该往前放),然后检验两个迭代器是否交错(first在左,last在右则不交错)。未交错则元素互相交换,然后各自调整一个位置,再继续相同行为。若交错,则以此时first为轴将序列分为左右两半,左边值都小于或等于pivot,右边都大于等于pivot。
template <class RandomAccessIterator, class T>
RandomAccessIterator __unguarded_partition(RandomAccessIterator first,
RandomAccessIterator last,
T pivot) {
while (true) {
while (*first < pivot) ++first;
--last;
while (pivot < *last) --last;
if (!(first < last)) return first;
iter_swap(first, last);
++first;
}
}
introsort
不当的枢轴选择,导致不当的分割,导致Quick Sort恶化为O(N^2)。David R. Musser于1996年提出一种混合式排序算法,Introspective Sorting。其行为在大部分情况下几乎与 median-of-3 Quick Sort完全相同。但是当分割行为(partitioning)有恶化为二次行为倾向时,能自我侦测,转而改用Heap Sort,使效率维持在O(NlogN),又比一开始就使用Heap Sort来得好。下面就是SGI STL源代码中堆IntroSort的实现。
SGI STL 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);
//经过上面的__introsort_loop已经排的差不多了,下面使用插入排序效率较高
__final_insertion_sort(first, last);
}
}
其中的__lg()用来控制分割恶化的情况:
找出2^k <= n的最大值k。比如:n=20,得k=4。
template <class Size>
inline Size __lg(Size n) {
Size k;
for (k = 0; n > 1; n >>= 1) ++k;
return k;
}
当元素个数为40时,__introsoft_loop()的最后一个参数将是5*2,意思是最多允许分割10层。
const int __stl_threshold = 16;
//本函数内的许多迭代器运算操作,都只适用于RandomAccess Iterators
emplate <class RandomAccessIterator, class T, class Size>
void __introsort_loop(RandomAccessIterator first,
RandomAccessIterator last, T*,
Size depth_limit) {
while (last - first > __stl_threshold) {
//depth_limit==0说明分割恶化,改用heapsort
if (depth_limit == 0) {
//进行堆排序
partial_sort(first, last, last);
return;
}
--depth_limit;
//三点中值决定函数,选择一个较好的枢纽作为分割点,分割点将落在迭代器cur身上
RandomAccessIterator cut = __unguarded_partition
(first, last, T(__median(*first, *(first + (last - first)/2),
*(last - 1))));
//堆右半段递归进行sort
__introsort_loop(cut, last, value_type(first), depth_limit);
last = cut;
//现在回到while循环,准备对左半段递归进行sort
}
}
当__introsort_loop()结束,[first,last)内有多个“元素个数少于16”的子序列,每个子序列都经过了相当程度的排序,但尚未完全排序(因为元素个数一旦小于__stl_threshold,就被终止进一步的排序操作了)。然后进入函数__final_insertion_sort()。
判断元素个数是否大于16。如果答案为否,就调用__insertion_sort()加以处理。否则就将[first,last)分割为长度为16的一段子序列和另一端剩余子序列,再针对两个子序列分别调用__insertion_sort()和__unguarded_insertion_sort()。
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);
}
template <class RandomAccessIterator>
inline void __unguarded_insertion_sort(RandomAccessIterator first,
RandomAccessIterator last) {
__unguarded_insertion_sort_aux(first, last, value_type(first));
}
template <class RandomAccessIterator, class T, class Compare>
void __unguarded_insertion_sort_aux(RandomAccessIterator first,
RandomAccessIterator last,
T*, Compare comp) {
for (RandomAccessIterator i = first; i != last; ++i)
//代码在前面的快速排序中
__unguarded_linear_insert(i, T(*i), comp);
}