快速排序的三种版本及优化

版本一:Hoare版本

单趟排序的思想:

一般让左边做key

左边做key,右边先走(原因:保证了相遇位置比key小)(if右边做key,左边先走)

右边先走,找小的,左边再走,找大的,然后交换。重复,直至相遇,相遇的值与key交换

单趟排序的意义:

1.分割出了左右区间:左边的值比key要小,右边的值比key要大

2.key已经落到了正确的位置,即排序后的正确位置

剩下的问题:将左区间有序、将右区间有序,用递归实现

代码实现:

void QuickSort(int* a, int begin,int end)//Hoare版本
{
	if (begin >= end)
		return;
	int left=begin;
	int right=end;
	int keyi = left;
	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;

	QuickSort(a, begin,keyi-1);
	QuickSort(a, keyi+1, end);

}

理想状态下:时间复杂度O(N*logN)—每次的key都能移到正中间

最坏情况下:时间复杂度O(N^2)顺序和逆序都是—每次的key都是最大或最小

优化一:三数取中(中间大小的那个数)

面对接近有序或有序的情况下快排就没有优势,那么提出优化:三数取中(中间大小的那个数):最左边、最右边、中间值,取中间大小(不是最大也不是最小)的那个值,再跟最左边的数交换,最左边的数还是做key。

加入三数取中后,快排几乎不会出现最坏情况,时间复杂度:O(N*logN)

优化二:小区间优化

递归调用中,底层的调用次数太多(比如:只排10个数时,递归了10多次),太浪费、耗时、耗栈,不如此时用插入排序 ,此时减少了80%-90%的递归次数

代码如下:

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

	//小区间用直接插入代替
	if ((end - begin + 1) < 10)
	{
		InsertSort(a+begin,end-begin+1);
	}
	else
	{
		//三数取中
		int mid = GetMidIndex(a, begin, end);
		Swap(&a[begin], &a[mid]);

		int left = begin;
		int right = end;
		int keyi = left;
		while (left < right)
		{
			//左边做key,右边先走;
			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;

		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
 
	}
	
}

版本二:挖坑法

单趟排序的思想:

第一步:将第一个数据(选最左边)放在临时变量key中,形成一个坑位。

第二步:(左边做key,右边先走)右边找小,把小的放到坑里,自己的这个位置形成新坑。

第三步:左边找大,把大的放到坑里,自己的位置形成新坑。

反复第二、第三步,直至左右相遇——一定相遇在坑位

最后一步:把key放到坑位里

剩下的问题:将左区间有序、将右区间有序,用递归实现

代码实现:

void QuickSort(int* a, int begin, int end)//挖坑法
{
	if (begin >= end)
		return;
	int left = begin;
	int right = end;
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		while (left < right&&a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right&&a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;

	QuickSort(a, begin, hole - 1);
	QuickSort(a, hole + 1, end);

}

版本三:双指针法

单趟:

左边做key,一前一后两个指针,cur和prev

第一步:cur找比key小,找到小后停下

第二步:prev++,交换prev位置和cur位置的值

重复第一、二步,直至cur走完

最后一步:交换prev位置的值和key

剩下的问题:将左区间有序、将右区间有序,用递归实现

代码实现:

void QuickSort(int* a, int begin, int end)//双指针法
{
	if (begin >= end)
		return;
	int keyi = begin;
	int prev = begin;
	int cur = begin + 1;
	while (cur<=end)
	{
		//找到比key小的值时,跟++prev位置交换,小的往前翻,大的往后翻
		if (a[cur] < a[keyi])
		{
			Swap(&a[++prev], &a[cur]);
		}

		cur++;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;

	QuickSort(a, begin, keyi- 1);
	QuickSort(a, keyi + 1, end);

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值