看看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);
	}




源码确实看着有些吃力,好歹是看完了……




评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朱铭德

五毛也是爱٩(●´৺`●)૭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值