一、sort()的潜在隐患
sort()最坏情况的改进
快速排序有着O(NlgN)的平均时间复杂度,如果遇到极端的情况可能会恶化到N^2。
但是在STL中,已经针对快速排序可能出现:
(1)pivot选取不当;
(2)递归深度过深;
两个问题分别给出了:
(1)三中值法:保证pivot选取合适;
(2)递归深度判断,过深直接堆排序。
将快速排序基本保持在了O(NlgN),有效地避免了最坏情况地发生。
但是仔细分析源码,下面是分割的具体实现:
while (*__first < __pivot)//当左值小于pivot,f向后移动,当左值大于等于pivot,停止
++__first;
--__last;
while (__pivot < *__last)////当右值大于pivot,l向前移动,当右值小于等于pivot,停止
--__last;
if (!(__first < __last))//如果两个指针接错,循环停止
return __first;
iter_swap(__first, __last);//否则交换
++__first;//指针向后移动一格
两个while分别用<和>进行了判断,
注意》》》》》如果此时first和last的值都等于pivot,那么两者仍然需要进行交换,当然两个相等的值交换并不影响什么,但是如果值还附带不同的属性呢,然后属性和值一同打包为一个对象,显然我们还是希望每次排序都是一样的。
这就是快速排序的不稳定的问题,为此STL中还有稳定版的sort。
二、stable_sort
快速排序不稳定,如果想要稳定的排序就换一种排序方式。
在STL中的其实就是归并排序,但是归并排序由于需要额外的数组来执行归并,所以如果没有足够的空间是不能正常归并的,STL是这样实现的:
template <class _RandomAccessIter, class _Tp, class _Distance, class _Compare>
inline void __stable_sort_aux(_RandomAccessIter __first, _RandomAccessIter __last, _Tp*, _Distance*,_Compare __comp) {
_Temporary_buffer<_RandomAccessIter, _Tp> buf(__first, __last);//开辟一个缓冲数组
if (buf.begin() == 0)//开辟失败了
__inplace_stable_sort(__first, __last, __comp);//调用内部归并
else
__stable_sort_adaptive(__first, __last, buf.begin(),_Distance(buf.size()), __comp);//有足够的空间,执行归并。时间复杂度在O(NlgN)
}
1、先看正常的有足够空间的归并吧:
首先,考虑的是数组我是申请了,但是万一不够大呢,于是就递归啊,直到大小合适了。
template <class _RandomAccessIter, class _Pointer, class _Distance,
class _Compare>
void __stable_sort_adaptive(_RandomAccessIter __first, _RandomAccessIter __last, _Pointer __buffer, _Distance __buffer_size, _Compare __comp) {
_Distance __len = (__last - __first + 1) / 2;
_RandomAccessIter __middle = __first + __len;
if (__len > __buffer_size) {//判断缓冲的数组是否足够使用
__stable_sort_adaptive(__first, __middle, __buffer, __buffer_size, __comp);//递归,将数组变小
__stable_sort_adaptive(__middle, __last, __buffer, __buffer_size, __comp);
}
else {
__merge_sort_with_buffer(__first, __middle, __buffer, (_Distance*)0, __comp);//递归划分左边
__merge_sort_with_buffer(__middle, __last, __buffer, (_Distance*)0,__comp);//划分右边
}
__merge_adaptive(__first, __middle, __last, _Distance(__middle - __first), _Distance(__last - __middle), __buffer, __buffer_size,__comp);//合并左右两边
}
现在,终于向CPU申请了足够大的内存空间,开始进入merge正题:
__merge_sort_with_buffer
和一般的归并递归实现不一样,这里在底层用了插入排序。
template <class _RandomAccessIter, class _Pointer, class _Distance,class _Compare>
void __merge_sort_with_buffer(_RandomAccessIter __first, _RandomAccessIter __last, _Pointer __buffer,_Distance*, _Compare __comp) {
_Distance __len = __last - __first;
_Pointer __buffer_last = __buffer + __len;
_Distance __step_size = __stl_chunk_size;
__chunk_insertion_sort(__first, __last, __step_size, __comp);//先调用chunk_insertion_sort对每个长度为_S_chunk_size的子区间进行插入排序。
while (__step_size < __len) {
__merge_sort_loop(__first, __last, __buffer, __step_size, __comp);//
__step_size *= 2;
__merge_sort_loop(__buffer, __buffer_last, __first, __step_size, __comp);
__step_size *= 2;
}
}
先调用chunk_insertion_sort对每个长度为_S_chunk_size的子区间进行插入排序。
__chunk_insertion_sort(__first, __last, __chunk_size)
{
while (__last - __first >= __chunk_size) {
__insertion_sort(__first, __first + __chunk_size);
__first += __chunk_size;
}
__insertion_sort(__first, __last);
}
合并已经插入排序好的分片
template <class _RandomAccessIter1, class _RandomAccessIter2,class _Distance, class _Compare>
void __merge_sort_loop(_RandomAccessIter1 __first,
_RandomAccessIter1 __last,
_RandomAccessIter2 __result, _Distance __step_size,_Compare __comp) {
_Distance __two_step = 2 * __step_size;
while (__last - __first >= __two_step) {
__result = merge(__first, __first + __step_size,__first + __step_size, __first + __two_step,__result,__comp);
__first += __two_step;
}
__step_size = min(_Distance(__last - __first), __step_size);
merge(__first, __first + __step_size,__first + __step_size, __last,__result,__comp);
}
其中的merge就比较正常了:
template <class _InputIter1, class _InputIter2, class _OutputIter>
_OutputIter merge(_InputIter1 __first1, _InputIter1 __last1,_InputIter2 __first2, _InputIter2 __last2,
_OutputIter __result) {
__STL_REQUIRES(_InputIter1, _InputIterator);
__STL_REQUIRES(_InputIter2, _InputIterator);
__STL_REQUIRES(_OutputIter, _OutputIterator);
__STL_REQUIRES_SAME_TYPE(
typename iterator_traits<_InputIter1>::value_type,
typename iterator_traits<_InputIter2>::value_type);
__STL_REQUIRES(typename iterator_traits<_InputIter1>::value_type,
_LessThanComparable);
while (__first1 != __last1 && __first2 != __last2) {
if (*__first2 < *__first1) {
*__result = *__first2;
++__first2;
}
else {
*__result = *__first1;
++__first1;
}
++__result;
}
return copy(__first2, __last2, copy(__first1, __last1, __result));
}
现在可以先总结一下:
首先如果数组nums的长度为len,首尾指针为left,right;
(1)首先,先判断是否有足够的缓冲数组产生;
假设有足够内存空间,那么就将nums从中间分块,划分为左右两块,每块上len/2,然后进入__merge_sort_with_buffer;
(2)对每一块以step_size又进行划分,并且调用插入排序,对每一分区进行排序;
len/2 = step_size+step_size+…….+ residual;
此时该原本长度为len/2的数组被划分为若干step_size大小以及余数的有序分区。
(3)先是同样都是step_size大小的有序数组进行归并,然后再与可能的余数部分进一步归并。最开始分得左右两边都已经是有序数组了。
开始合并最后的有序数组:
template <class _BidirectionalIter, class _Distance, class _Pointer>
void __merge_adaptive(_BidirectionalIter __first,
_BidirectionalIter __middle,
_BidirectionalIter __last,
_Distance __len1, _Distance __len2,
_Pointer __buffer, _Distance __buffer_size) {
if (__len1 <= __len2 && __len1 <= __buffer_size) {//如果缓冲数组足够大
_Pointer __buffer_end = copy(__first, __middle, __buffer);//将前半部分复制到缓冲数组上
merge(__buffer, __buffer_end, __middle, __last, __first);//合并到first
}
else if (__len2 <= __buffer_size) {
_Pointer __buffer_end = copy(__middle, __last, __buffer);
__merge_backward(__first, __middle, __buffer, __buffer_end, __last);//__merge_backward与std::merge类似
}
else {//如果缓冲数组不够大怎么办
_BidirectionalIter __first_cut = __first;
_BidirectionalIter __second_cut = __middle;
_Distance __len11 = 0;
_Distance __len22 = 0;
if (__len1 > __len2) {
__len11 = __len1 / 2;
advance(__first_cut, __len11);
__second_cut = lower_bound(__middle, __last, *__first_cut);
distance(__middle, __second_cut, __len22);
}
else {
__len22 = __len2 / 2;
advance(__second_cut, __len22);
__first_cut = upper_bound(__first, __middle, *__second_cut);
distance(__first, __first_cut, __len11);
}
_BidirectionalIter __new_middle =
__rotate_adaptive(__first_cut, __middle, __second_cut, __len1 - __len11, __len22, __buffer, __buffer_size);
__merge_adaptive(__first, __first_cut, __new_middle, __len11,__len22, __buffer, __buffer_size);//递归处理
__merge_adaptive(__new_middle, __second_cut, __last, __len1 - __len11,__len2 - __len22, __buffer, __buffer_size);
}
}