实现快速排序的三种重要方法以及改进

   左右指针法(hoare版本)

这个方法就是在数组的首与尾设置左右指针,并在首处设置keyi

接下来移动右指针,如果当前指针指向的数值小于keyi的数值,则--right,左指针同理,如果left的值大于keyi,++left。如果达成条件,则交换left跟right的值,直到left跟right相遇。

当单趟结束时,keyi的值就会到达它最终的位置

此时数组已经是相对有序的状态,我们接下来递归这个函数,直到区间不存在或者只剩下一个值,此时数组将是有序状态。 

接下来是具体的代码实现 

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void QuickSort(int* a, int left, int right)
{
	//如果区间不存在或只剩一个值,则返回上一层
	if (left >= right)
		return;

	int keyi = left;
	int begin = left, end = right;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])
			right--;
		while (left < right && a[keyi] >= a[left])
			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);
}

这是最淳朴的快排代码,时间复杂度是O(N*logN)

我们可以设想一下,在一个巨大的数组面前,如果开始时数组相对有序,则很有可能是最坏情况 ,可以理解为单趟下keyi只移动了很小一段距离,如此下来,函数将递归非常深的层数,此时代码很有可能崩溃,为了解决这种情况,又分出了两种方法,随机数法以及三数选中

随机数法相对简单,我直接展示代码

​
void QuickSortRand(int* a, int left, int right)
{
	if (left >= right)
		return;

	int begin = left, end = right;

    //把randi的距离限制在区间内
	int randi = rand() % (right - left);
	randi += left;
	Swap(&a[randi], &a[left]);

	int keyi = left;
	
	while (left < right)
	{
        //right找小
		while (left < right && a[keyi] <= a[right])
			--right;
		//left找大
		while (left < right && a[keyi] >= a[left])
			++left;
		Swap(&a[left], &a[right]);

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

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

​

这样的话keyi在数组相对靠中的概率将大大增大,快排效率也会有些许提高

三数靠中法相对麻烦,我们需要编写一个GetMid函数,来取得left,right,mid三数中在数组中代表中间的值,以下是代码实现

int GetMid(int* a, int left, int right)//三数取中
{
	int mid = (left + right) / 2;
	if (a[mid] < a[left])//mid至少第二小
	{
		if (a[mid] > a[right])//a[right]最小
		{
			return mid;
		}
		else//a[mid]最小 
		{
			if (a[right] > a[left])
				return left;
			else
				return right;
		}
	}
	else//a[mid]>=a[left]
	{
		if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			if (a[mid] > a[right])
			{
				return right;
			}
			else {
				return mid;
			}
		}
	}
}

接下来介绍前后指针法

前后指针法相对于左右指针法的最大优势是实现相对简单,我将结合代码进行讲解

void QuickSort2(int* a, int left, int right)
{
	if (left >= right)
		return;

	int prev = left, cur = left + 1;
	int keyi = left;
	while (cur <= end)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
			Swap(&a[prev], &a[cur]);
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	keyi = prev;
	QuickSort2(a, left, keyi - 1);
	QuickSort2(a, keyi + 1, right);
}

前后指针版最大的优势就是代码简洁了不少,效率方面并无太大差异。 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值