vs2013 std::sort 分析
2015-04-24 06:43
274 查看
由于之前在debug模式下发现stl的sort简直慢到不能忍,所以自己写了一个sgi的sort,后来发现在release模式下,vs自带的sort快的不行,就研究了下。
这里有些和sgi-stl相通的东西就简略带过了,详细内容可以看我之前的stl源码的笔记:
sgi-sort_link
首先来看下大概的过程:
接下来是详细分析:
1:当我们调用sort(frist,last);时,程序就来到下面的代码,帮忙添加一个参数:less<>()默认用<来比较。
2:接着来到,这时会调用真正的sort,也就是_Sort
3:然后进入真正的sort:这里进行说明
4:下面看看重头戏:
到此完结,能看到这相信你肯定已经懂了哈,有不对的地方希望大神指出,谢谢!
这里有些和sgi-stl相通的东西就简略带过了,详细内容可以看我之前的stl源码的笔记:
sgi-sort_link
首先来看下大概的过程:
1.没有调用到一定深度时,就进行划分并进行递归调用。 2.如果超过了一定深度时,这个区间改为调用堆排序。(这一部待商榷) 3.对剩下的小于32长度的区间进行插入排序。
接下来是详细分析:
1:当我们调用sort(frist,last);时,程序就来到下面的代码,帮忙添加一个参数:less<>()默认用<来比较。
template<class _RanIt> inline void sort(_RanIt _First, _RanIt _Last) { // order [_First, _Last), using operator< _STD sort(_First, _Last, less<>()); }
2:接着来到,这时会调用真正的sort,也就是_Sort
template<class _RanIt, class _Pr> inline void sort(_RanIt _First, _RanIt _Last, _Pr _Pred) { // order [_First, _Last), using _Pred _DEBUG_RANGE(_First, _Last); _DEBUG_POINTER(_Pred); _Sort(_Unchecked(_First), _Unchecked(_Last), _Last - _First, _Pred); }
3:然后进入真正的sort:这里进行说明
template<class _RanIt, class _Diff, class _Pr> inline void _Sort(_RanIt _First, _RanIt _Last, _Diff _Ideal, _Pr _Pred) { // order [_First, _Last), using _Pred _Diff _Count; //_Count是计算出来的传入的元素个数,貌似为了安全,还要传入一个元素个数的参数 //也就是_Ideal for (; _ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal; ) { // divide and conquer by quicksort // 这个循环的原因是为了继续划分 pair<_RanIt, _RanIt> _Mid = _Unguarded_partition(_First, _Last, _Pred); _Ideal /= 2, _Ideal += _Ideal / 2; // allow 1.5 log2(N) divisions //如果左边区间比较小就递归排序左边的区间,然后继续划分右边的区间 //否则排序右边,划分左边 if (_Mid.first - _First < _Last - _Mid.second) { // loop on second half _Sort(_First, _Mid.first, _Ideal, _Pred); _First = _Mid.second; } else { // loop on first half _Sort(_Mid.second, _Last, _Ideal, _Pred); _Last = _Mid.first; } } //划分到区间小于_ISORT_MAX //深度太深,改为堆排序。为什么我感觉这是永远不会执行到的因为_ISORT_MAX <_Count //不是上一个循环的结束判断条件么 //而且在release模式下,我设断点都会直接跳过,并不执行if的判断 //所以目测是不会执行的 if (_ISORT_MAX < _Count) { // heap sort if too many divisions _STD make_heap(_First, _Last, _Pred); _STD sort_heap(_First, _Last, _Pred); } else if (1 < _Count)//到了底层,就调用插入排序了,奇怪的是为什么不学sgi的插入排序,可以 //节省很多次的判断。 _Insertion_sort(_First, _Last, _Pred); // small }
4:下面看看重头戏:
template<class _RanIt, class _Pr> inline pair<_RanIt, _RanIt> _Unguarded_partition(_RanIt _First, _RanIt _Last, _Pr _Pred) { // partition [_First, _Last), using _Pred //找到最中间的元素。 _RanIt _Mid = _First + (_Last - _First) / 2; //_Median这个函数的作用是把头中尾这三个数进行排序,还会把头中尾附近三个左右的元素进行排序 //具体不必关心,这里只要知道头中尾三个元素是排好序的。而且_Mid就是关键划分元素 _Median(_First, _Mid, _Last - 1, _Pred); //_Pfirst 是左半部分的last,_Plast 是右半部分的first值 //这里虽然看起来first和last颠倒了,其实这两个值的意思是[_Pfirst, _Plast)区间的元素值 //全部是_Mid的值("_Mid指向的值"下面简称:mid); _RanIt _Pfirst = _Mid; _RanIt _Plast = _Pfirst + 1; //有重复的值就左移或右移动来扩大区间最后达到的效果是这样的[_Pfirst..一堆mid.._Plast) //注意_Pfirst和_Plast-1指向的值也是mid while (_First < _Pfirst && !_DEBUG_LT_PRED(_Pred, *(_Pfirst - 1), *_Pfirst) && !_Pred(*_Pfirst, *(_Pfirst - 1))) --_Pfirst; while (_Plast < _Last && !_DEBUG_LT_PRED(_Pred, *_Plast, *_Pfirst) && !_Pred(*_Pfirst, *_Plast)) ++_Plast; //这两个值分别是未划分的左边部分的尾部:_Glast 以及右半部分的头部:_Gfirst _RanIt _Gfirst = _Plast; _RanIt _Glast = _Pfirst; //这时完整的区间是这样的[_First....[_Pfirst(同时也是_Glast)..._Plast) //...._Last)!!注意这里是开区间哈 _Plast也是_Gfirst,为了好看,就写在这了 for (; ; ) { // 划分开始了。。。 for (; _Gfirst < _Last; ++_Gfirst)//先进行边界判断 { //从右半边开始,只要_Gfirst指向的值(下面指向的值都去掉“指向的值”这四个字来简称) //比_Pfirst(也就是mid)大就一直++ //直到找到比_Pfirst小的值这样就可以和_Pfirst-1的元素进行交换了 if (_DEBUG_LT_PRED(_Pred, *_Pfirst, *_Gfirst)) ; else if (_Pred(*_Gfirst, *_Pfirst))//找到 break; //一个小插曲:如果找到和mid一样大的值就把这个值换到_Plast++的位置来保持 //[_Pfirst, _Plast)区间全是mid; else if (_Plast++ != _Gfirst)//如果两个位置相同就不用换了 _STD iter_swap(_Plast - 1, _Gfirst); } //同上面,相应的左区间的操作,最后找到大于mid的位置。 //只要注意stl右边都是开区间的,_Glast-1才是前面区间的最后一个值 for (; _First < _Glast; --_Glast) { if (_DEBUG_LT_PRED(_Pred, *(_Glast - 1), *_Pfirst)) ; else if (_Pred(*_Pfirst, *(_Glast - 1))) break; else if (--_Pfirst != _Glast - 1) _STD iter_swap(_Pfirst, _Glast - 1); } //达到边界就返回一个pair指向mid区间 //这里的判断是这样的: //1:_Glast 是左边未划分区间的最后一个元素的后一个位置所以_First之前 //未划分_First开始,已经划分。 //2:_Gfirst 代表右边未划分的第一个元素,而_Last是我们需要划分的区间的最后一个 //元素的后一个位置,所以如果下面条件成立,[_First ,_Last)划分完成 if (_Glast == _First && _Gfirst == _Last) return (pair<_RanIt, _RanIt>(_Pfirst, _Plast)); //左边全部划分完了,看起来像这样[_First(_Glast,)..._Pfirst..............) //这时如果要交换左右两边的元素就需要特殊处理了 if (_Glast == _First) { // no room at bottom, rotate pivot upward //情况1:如果_Plast ==_Gfirst说明_Gfirst就是mid区间的last位置 //这时只需要交换_Pfist和_Gfirst就可以把mid区间右移一格 if (_Plast != _Gfirst) _STD iter_swap(_Pfirst, _Plast); //情况2:先把一个mid换到最后面,这时_Pfirst指向一个大于mid的值,_Gfirst是指向小于mid的值的。 //++_Plast, 因为中间全是mid的区间要整个后移一格 ++_Plast; //情况1:交换_Pfist和_Gfirst相当于把第一个mid值和右边大于mid的值交换 //情况2:交换_Pfirst,_Gfirst,刚好把小的换到左边,大的换到右边 _STD iter_swap(_Pfirst++, _Gfirst++); } else if (_Gfirst == _Last) { // no room at top, rotate pivot downward if (--_Glast != --_Pfirst) _STD iter_swap(_Glast, _Pfirst); _STD iter_swap(_Pfirst, --_Plast); } else //交换左右区间元素。 _STD iter_swap(_Gfirst++, --_Glast); } //这个其实就是双基准快排的实现,相对于普通快排最后得到的结果就是把_Mid元素都集中在 //一起了. }
到此完结,能看到这相信你肯定已经懂了哈,有不对的地方希望大神指出,谢谢!
相关文章推荐
- vs2013 std::sort 分析
- 基于比较的排序总结-结合qsort&&std::sort分析
- VS2013/MFC编程入门之三(MFC应用程序框架分析)
- 使用std::sort()排序导致程序core问题分析
- 排序算法:qsort vs std::sort
- 排序算法:qsort vs std::sort
- VS2013 max 不是 std 成员
- 排序算法:qsort vs std::sort
- 【OpenCV图像处理入门学习教程一】OpenCV2 + 3的安装教程与VS2013的开发环境配置 + JPEG压缩源码分析与取反运算修改
- VS2013 解决方案文件结构分析
- 使用std::sort()排序导致程序core问题分析
- VS2013 解决方案文件结构分析
- vs2013编译g3dlite时出现 min不是std的成员 mangos
- 深入分析qsort库函数:std::sort和qsort的比较
- 使用std::sort()排序导致程序core问题分析
- VS2013 Brower Link和Aspnetpager引发的问题分析
- win32模版文件分析(VS2013)
- VS2013单元测试及代码覆盖率分析--Xunit
- 使用std::sort()排序导致程序core问题分析
- VS2013 bug : 无法使用std::function包装成员函数