快速排序hoare优化

目录

三数取中法选key

优化点

基本思想 

代码实现 

小区间优化

优化点

基本思想

代码实现


由于hoare版快排在一些特殊情况下性能并不优,这里我们进行一些优化。 

三数取中法选key

优化点

  • 当数据有序时,快排就会很吃力,这是为什么呢?

因为有序时,left要找小,right要找大,都找不到,就会一直从头找到尾,时间复杂度就会上升到O(N^2)。在debug版本下,如果数据上万,就会栈溢出。

基本思想 

  • 三数取中:取三个数中的中位数下标,选到合适的数,避免选取到最小的最大的数,对顺序结构的效率优化很好。
  • 取的是下标❗
  • 数值两两比较
  • 对快速排序的单趟的优化,三数取中是走快速排序逻辑(每趟的优化)

代码实现 

int GetMidi(int* a, int begin, int end)
{
	int midi = (begin + end) / 2;
	// begin end midi三个数选中位数
	if (a[begin] < a[midi])
	{
		if (a[midi] < a[end])
			return midi;
		else if (a[begin] > a[end])
			return begin;
		else
			return end;
	}
	else
	{
		if (a[midi] > a[end])
			return midi;
		else if (a[begin] < a[end])
			return begin;
		else
			return end;
	}
}
 
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
 
	int midi = GetMidi(a, begin, end);
	Swap(&a[midi], &a[begin]);
 
	int left = begin, right = end;
	int keyi = begin;
 
	while (left < right)
	{
		// 右边找小
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}
 
		// 左边找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
 
		Swap(&a[left], &a[right]);
	}
 
	Swap(&a[left], &a[keyi]);
	keyi = left;
 
	// [begin, keyi-1] keyi [keyi+1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);
}

小区间优化

优化点

  • 当快排递归到小的子区间时,比如就只有7个数据,我们却还需要再递归7次才能返回结果。没有必要。
  • 当数据很少时,用希尔/堆排/快排都很杀鸡用牛刀,这个时候我们只需用直接插入排序即可。

也就是说,当快排递归到小的子区间时,我们考虑使用插入排序,这就是小区间优化。

基本思想

  • 主要优化递归层数的最后3~4层
  • 最后3~4层的递归层数占据了总的递归层数的80%。

  • 区间是[begin,end] ,此区间的数据量是end-begin+1。当数据量end-begin+1<=10的时候,让序列执行直接插入排序
  • 每段数据序列被分割了,起始位置并不都是a,要用a+begin表示

代码实现

int GetMidi(int* a, int begin, int end)
{
	//...
}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

	if (end - begin + 1 <= 10)
	{
		InsertSort(a + begin, end - begin + 1);
	}
	else
	{
		//三数取中的代码...
	}
}
  • 注意:小区间优化在release版本下,并不会与没优化的快多少,因为release已经优化了很多了。

hoare版本总代码

void InsertSort(int* a, int n)
{
	//[0,end]end+1插入
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

int GetMidi(int* a, int begin, int end)
{
	int midi = (begin + end) / 2;
	// begin midi end 三个数选中位数
	if (a[begin] < a[midi])
	{
		if (a[midi] < a[end])
			return midi;
		else if (a[begin] > a[end])
			return begin;
		else
			return end;
	}
	else // a[begin] > a[midi]
	{
		if (a[midi] > a[end])
			return midi;
		else if (a[begin] < a[end])
			return begin;
		else
			return end;
	}
}

int PartSort1(int* a, int begin, int end)
{
	int midi = GetMidi(a, begin, end);
	Swap(&a[midi], &a[begin]);

	int left = begin, right = end;
	int keyi = begin;

	while (left < right)
	{
		// 右边找小
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}

		// 左边找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}

		Swap(&a[left], &a[right]);
	}

	Swap(&a[left], &a[keyi]);

	keyi = left;
	return keyi;
}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;

    //<10个数字走插入逻辑 
	if (end - begin + 1 <= 10)
	{
		InsertSort(a + begin, end - begin + 1);
	}
    else
    {
	    int keyi = PartSort1(a, begin, end);
	    QuickSort(a, begin, keyi - 1);
	    QuickSort(a, keyi+1, end);
    }
}

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值