C++编程之stable_sort

来源:互联网 发布:中国网络信息安全联盟 编辑:程序博客网 时间:2024/06/11 10:17

一、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);  }}
原创粉丝点击