vs2013 std::sort 分析
由于之前在debug模式下发现stl的sort简直慢到不能忍,所以自己写了一个sgi的sort,后来发现在release模式下,vs自带的sort快的不行,就研究了下。
这里有些和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 分析的更多相关文章
- 一个std::sort 自定义比较排序函数 crash的分析过程
两年未写总结博客,今天先来练练手,总结最近遇到的一个crash case. 注意:以下的分析都基于GCC4.4.6 一.解决crash 我们有一个复杂的排序,涉及到很多个因子,使用自定义排序函数的st ...
- c++ std::sort函数调用经常出现的invalidate operator<错误原因以及解决方法
在c++编程中使用sort函数,自定义一个数据结构并进行排序时新手经常会碰到这种错误. 这是为什么呢?原因在于什么?如何解决? 看下面一个例子: int main(int, char*[]) { st ...
- std::sort引发的core
#include <stdio.h> #include <vector> #include <algorithm> #include <new> str ...
- 非常无聊——STD::sort VS 基数排序
众所周知,Std::sort()是一个非常快速的排序算法,它基于快排,但又有所修改.一般来说用它就挺快的了,代码一行,时间复杂度O(nlogn)(难道不是大叫一声“老子要排序!!”就排好了么...). ...
- 将三维空间的点按照座标排序(兼谈为std::sort写compare function的注意事项)
最近碰到这样一个问题:我们从文件里读入了一组三维空间的点,其中有些点的X,Y,Z座标只存在微小的差别,远小于我们后续数据处理的精度,可以认为它们是重复的.所以我们要把这些重复的点去掉.因为数据量不大, ...
- 源码阅读笔记 - 1 MSVC2015中的std::sort
大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格 ...
- shuffle和sort分析
MapReduce中的Shuffle和Sort分析 MapReduce 是现今一个非常流行的分布式计算框架,它被设计用于并行计算海量数据.第一个提出该技术框架的是Google 公司,而Google 的 ...
- Qt使用std::sort进行排序
参考: https://blog.csdn.net/u013346007/article/details/81877755 https://www.linuxidc.com/Linux/2017-01 ...
- MapReduce中的Shuffle和Sort分析
MapReduce 是现今一个非常流行的分布式计算框架,它被设计用于并行计算海量数据.第一个提出该技术框架的是Google 公司,而Google 的灵感则来自于函数式编程语言,如LISP,Scheme ...
随机推荐
- C/C++控制Windows关机/注销/重启的正确姿势
简介 说到代码控制Windows关机/注销/重启的方式,有很多种,最简单的不过就是控制命令行,使用system("pause")函数执行一个shutdown -s -t 0,关机就 ...
- Python中日期时间案例演示
案例:准备10个人姓名,然后为这10个人随机生成生日[都是90后] 1.统计出那些人是夏季[6月-8月]出生的. 2.最大的比最小的大多少天 3.谁的生日最早,谁的生日最晚 备注:春季[3-5]夏季[ ...
- Alpha冲刺&总结报告(12/12)(麻瓜制造者)
各个成员今日完成的任务 邓弘立: 完成了上传头像的功能 符天愉: 对所有接口进行了再次测试 江郑: 完成了发布需求接口部分的进一步测试和接口文档的编写 刘双玉: 完成了商品信息接口部分的进一步测试和接 ...
- Use MusicBrainz in iOS(三)查询专辑的完整信息
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u010962810/article/details/24066737 本文讨论下通过专辑名获取专辑的 ...
- 利用Python爬取豆瓣电影
目标:使用Python爬取豆瓣电影并保存MongoDB数据库中 我们先来看一下通过浏览器的方式来筛选某些特定的电影: 我们把URL来复制出来分析分析: https://movie.douban.com ...
- Project configuration is not up-to-date with pom.xml
导入maven工程后,出现如下错误: Description Resource Path Location TypeProject configuration is not u ...
- C#自定义控件开发
自定义控件开发 一般而言,Visual Studio 2005中自带的几十种控件已经足够我们使用了,但是,在一些特殊的需求中,可能需要一些特殊的控件来与用户进行交互,这时,就需要我们自己开发新的.满足 ...
- OpenCV——直方图计算、寻早最值位置和对比匹配(判断两幅图的相似程度)
- OpenCV——视频操作基础
读入视频: VideoCapture 类 //方法一 VideoCapture capture; capture.open("test.avi"); //方法二 VideoCapt ...
- 谷歌开源漏洞跟踪工具 Monorail 存在跨站点搜索漏洞
一名安全研究员表示,在谷歌开源漏洞跟踪工具 Monorail 中找到一个漏洞,可被用于执行跨站点搜索 (XS-Search) 攻击. Monorail 用于检查和 Chromium 相关项目中的问题, ...