看看C++快排(sort)源码
Posted 朱铭德
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了看看C++快排(sort)源码相关的知识,希望对你有一定的参考价值。
说到排序第一反应是直接调用sort函数
vector<int>test = 2,1,5,2,3,6,233,4 ;
sort(test.begin(), test.end());
当然也可以自己定义一个比较的函数:
bool more(int a, int b) return a > b;
vector<int>test = 2,1,5,2,3,6,233,4 ;
sort(test.begin(), test.end(),more);
非常方便简单,下面我们来看看sort函数的实现机制。
--------------------------------------------------------------------------------------------------------------
首先sort函数调用_STD sort
这里的less<>()其实是比较的机制,默认sort函数是升序,所以左<右
voidd sort(_RanIt _First, _RanIt _Last)
// order [_First, _Last), using operator<
_STD sort(_First, _Last, less<>());
_STD sort调用的是_Sort_unchecked
参数_Pr _Pred就是比较的方法……这里是升序的是否小于
当然我们也可以用三参数的sort函数,自己定义比较的方式……
void sort(_RanIt _First, _RanIt _Last, _Pr _Pred)
// order [_First, _Last), using _Pred
_DEBUG_RANGE(_First, _Last);
_Sort_unchecked(_Unchecked(_First), _Unchecked(_Last), _Pred);
_Sort_unchecked调用的_Sort_unchecked1(_First, _Last, _Last - _First, _Pred);
void _Sort_unchecked(_RanIt _First, _RanIt _Last, _Pr& _Pred)
// order [_First, _Last), using _Pred
_Sort_unchecked1(_First, _Last, _Last - _First, _Pred);
_Sort_unchecked1源码如下:
_Diff是两个迭代器相减的结果(可以认为是排序内容的长度)
1.当长度n小于32的时候,直接调用插入排序
2.当长度n大于32时,先用快排处理1.5*log2(n)次,如果长度还是大于32——说明这个数组不太适合用快排,所以调用堆排序来处理
3.处理1.5*log2(n)次后如果长度小于32,调用插入排序
4.处理快排用到下一个的函数_Partition_by_median_guess_unchecked
void _Sort_unchecked1(_RanIt _First, _RanIt _Last, _Diff _Ideal, _Pr& _Pred)
// 区间为 [_First, _Last)
_Diff _Count;//排序内容的长度
while (_ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal)
//const int _ISORT_MAX = 32;//大于32才开始快排,否则直接插入排序...
//当数组首尾的长度大于32的时候才开始愉快地快排,并且排的区间长度>0
// divide and conquer by quicksort
pair<_RanIt, _RanIt> _Mid =
_Partition_by_median_guess_unchecked(_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_unchecked1(_First, _Mid.first, _Ideal, _Pred);//迭代少的那一半
_First = _Mid.second;
else
// loop on first half
_Sort_unchecked1(_Mid.second, _Last, _Ideal, _Pred);
_Last = _Mid.first;
if (_ISORT_MAX < _Count)
// heap sort if too many divisions
_Make_heap_unchecked(_First, _Last, _Pred);
_Sort_heap_unchecked(_First, _Last, _Pred);
else if (2 <= _Count)
_Insertion_sort_unchecked(_First, _Last, _Pred); // small
主要作用:估计出中间值(“猜一猜”,具体策略——见下一个的函数),然后把数据分成小于中间值和大于中间值的两组,分别放在左边和右边
Mid的类型是pair<RanIt,RanIt>两个值分别为左半的尾巴以及右半的开始
值得注意的是要处理两种情况:
1.中间值相等,往mid里塞就好……
2.首尾没空间的时候——mid的两个值互换一下,再和另一端交换一下……
pair<_RanIt, _RanIt>
_Partition_by_median_guess_unchecked(_RanIt _First, _RanIt _Last, _Pr& _Pred)
// partition [_First, _Last), using _Pred
_RanIt _Mid = _First + (_Last - _First) / 2;//中间值
_Guess_median_unchecked(_First, _Mid, _Last - 1, _Pred);
_RanIt _Pfirst = _Mid;//Pfirst从中间开始
_RanIt _Plast = _Pfirst + 1;//Plast从中间后一位开始
while (_First < _Pfirst //Pfirst从中间往左还没遍历到开始
&& !_DEBUG_LT_PRED(_Pred, *(_Pfirst - 1), *_Pfirst)//如果是有效的,并且比较一下
&& !_Pred(*_Pfirst, *(_Pfirst - 1)))//这两条表示相等…
--_Pfirst;//左移一位
while (_Plast < _Last //Plast从中间往右挪
&& !_DEBUG_LT_PRED(_Pred, *_Plast, *_Pfirst)
&& !_Pred(*_Pfirst, *_Plast))//相等就接着往右挪一位
++_Plast;
_RanIt _Gfirst = _Plast;
_RanIt _Glast = _Pfirst;
for (; ; )
// partition
//移动Gfirst和Glast遍历
for (; _Gfirst < _Last; ++_Gfirst)
if (_DEBUG_LT_PRED(_Pred, *_Pfirst, *_Gfirst)) //如果Gfirst比Pfirst大,就继续,没毛病
;
else if (_Pred(*_Gfirst, *_Pfirst)) //如果Gfirst比Pfirst小...(假设规则为小于),退出,等着被换……
break;
else if (_Plast++ != _Gfirst) //相等了,所以就++一下(找到一个等于中间值的)
_STD iter_swap(_Plast - 1, _Gfirst);//往回拉一下
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);
if (_Glast == _First && _Gfirst == _Last)
return (pair<_RanIt, _RanIt>(_Pfirst, _Plast));
if (_Glast == _First)//处理尾部没空间的情况
// no room at bottom, rotate pivot upward
if (_Plast != _Gfirst)
_STD iter_swap(_Pfirst, _Plast);
++_Plast;
_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);//换一下。。。
其中_Guess_median_unchecked函数为:
1.当长度大于40的时候多分几段,这样比较靠谱…把最中间的挪到mid位置…
2.不太长的时候就三个位置比较及交换一下
3._Med3_unchecked函数的主要作用是把三个数按比较方式排列一下…(迭代器/指针交换
void _Guess_median_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr& _Pred)
// sort median element to middle
if (40 < _Last - _First)
// median of nine
size_t _Step = (_Last - _First + 1) / 8;
_Med3_unchecked(_First, _First + _Step, _First + 2 * _Step, _Pred);
_Med3_unchecked(_Mid - _Step, _Mid, _Mid + _Step, _Pred);
_Med3_unchecked(_Last - 2 * _Step, _Last - _Step, _Last, _Pred);
_Med3_unchecked(_First + _Step, _Mid, _Last - _Step, _Pred);
else
_Med3_unchecked(_First, _Mid, _Last, _Pred);
源码确实看着有些吃力,好歹是看完了……
以上是关于看看C++快排(sort)源码的主要内容,如果未能解决你的问题,请参考以下文章