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); }}
- C++编程之stable_sort
- C ++ sort()与stable_sort()
- C ++ sort()与stable_sort()
- stable_sort
- stable_sort
- stable_sort
- stable_sort()
- stable_sort
- STL之stable_sort与sort
- c、c++排序函数qsort、stable_sort
- 泛型算法之sort,unique,stable_sort
- C++排序之stable_sort()的方法
- STL算法之sort和stable_sort
- algorithm库介绍之---- stable_sort()方法 与 sort()方法
- C++泛型算法之 sort() stable_sort() unique() count_if()
- STL 之 stable_sort 将牌从大到小排序
- algorithm库介绍之---- stable_sort()方法 与 sort()方法
- algorithm库介绍之---- stable_sort()方法 与 sort()方法
- Bootstrap表单
- Windows编程入门-保存设备环境
- 汤森路透经典课程
- mysql cluster ndb 核心参数介绍(理解 ndb,先从使用开始,也透露出了实现) 脑裂相关
- Makefile
- C++编程之stable_sort
- ubuntu linux 下安装mysql详解
- django中 authenticate返回None的情况解决办法
- Android测试--Monkey
- Bootstrap网格系统
- Feign Hystrix 支持
- A^B Mod C(快速幂算法)
- PCL中ICP配准点云(计算出RT,并且设置不同颜色来显示三个点云)
- 双关系递推