【数据结构与算法】八大排序(中)快速排序 快排居然还能这么优化?快排的非递归该如何写?

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

今天带来的内容是快速排序

这里是下面要讲的知识内容🥳🥳🥳


一、⚽快速排序(原始方法)

1.🌈单趟代码与解释(升序)

选出一个key,一般是最左边或者最右边的数据,定义两个指针,左指针从左开始走,左指针找大,右指针从右开始走,右指针找小,然后Swap左指针和右指针的数据,当左指针和右指针相遇的时候,Swap key和该点的数据
保证在一趟走完之后,key的左边比key小,右边比key大

//单趟代码
void QuickSort(int *a,int n)
{
	int key=0;
	int left=0,right=n-1;
	while(left<rigth)
	{
		while(left<right&&a[right]>a[key])
			right--;
		while(left<right&&a[left]<a[key])
			left++;
		Swap(&a[left],&a[right]);
	}
	Swap(a[key],a[left]);
}

我们必须保证right要先走,解释如下:

right先走,到最后,right停下了之后,left++去碰right,因为right找到的是小的数据,如果说让left先走,这种情况下最后停下来的是比key大的数据
在这里插入图片描述

2.🌈递归完整代码&&时间复杂度分析

void QuickSort(int*a,int begin,int end)
{
	if()
		return ;
	int left=begin;
	int right=end;
	int keyi=left;
	while(left<rigth)
	{
		while(left<right&&a[right]>a[keyi])
			right--;
		while(left<right&&a[left]<a[keyi])
			left++;
		Swap(&a[left],&a[right]);
	}
	Swap(a[keyi],a[left]);
	keyi=left;
	QuickSort(a,begin,keyi-1);
	QuickSort(a,keyi+1,end);
}

时间复杂度O(NlogN)
最坏的情况是有序或者接近有序,O(N^2),N+N-1+N-2+…

二、⚽快速排序(挖坑法)

按升序来说
右边找小,填到左边的坑,这个位置成为新的坑,然后左边找大,填到这个坑当中

int PartSort(int *a,int begin,int end)
{
	int key=a[begin];
	int piti=begin;
	while(begin<end)
	{
		while(begin<end&&a[end]>=key)
			--end;
		a[piti]=a[end];
		piti=end;
		while(begin<end&&a[begin<=key])
			++begin;
		a[piti]=a[begin];
		piti=begin;
	}
	a[piti]=key;
	return piti;
}

void QuickSort(int *a,int begin,int end)
{
	if(begin>=end)
	return ;
	int keyi=PartSort(a,begin,end);
	QuickSort(a,begin,keyi-1);
	QuickSort(a,keyi+1,end);
}

三、⚽快速排序(前后指针版本)

思路就是prev指向更新数据的下一个,cur去找比key小的数据(升序)

int PartSort(int *a,int begin,int end)
{
	int prev,cur,key;
	prev=key=begin;
	cur=begin+1;
	while(cur<=end)
	{
		if(a[cur]<a[key])
		{
			++prev;
			Swap(&a[cur],&a[prev]);
		}
		++cur;
	}
	Swap(&a[prev],&a[key]);
	return prev;
}

void QuickSort(int *a,int begin,int end)
{
	if(begin>=end)
	return ;
	int keyi=PartSort(a,begin,end);
	QuickSort(a,begin,keyi-1);
	QuickSort(a,keyi+1,end);
}

四、⚽快速排序的优化

可以看出key的取值是很影响QuickSort的效率的,因此,我们需要对快速排序的算法进行优化

1、🌈随机选key

随便选一个当做key,可能选到最小的或者最大的那一个,不够好,因为快速排序最理想的就是二分,选到最中间的那个数。所以这种就不进行深度探索了

2、🌈三数取中

****

void GetMidIndex(int*a,int begin,int end)
{
	int mid=(begin+end)/2;
	if(a[begin]<a[end])
	{	
		if(a[mid]>a[end])
		return end;
		else if(a[mid]<a[begin])
		return begin;
		else
		return mid;
	}
	else
	{
		if(a[mid]>begin)
		return begin;
		else if(a[mid]<a[end])
		return end;
		else
		return mid;
	}
	//找到了之后和begin交换以下即可作为keyi
}

3、🌈处理小区间

因为递归是个很麻烦的事,当递归划分小区间,区间比较小的时候,就不再递归划分去排序这个小区间,可以考虑用其他排序对小区间处理,比如插入排序
这样做减少了非常多的递归次数,最后一层就占了总的递归次数的一半,如果能把最后3、4层都去掉,至少减掉了过半的递归次数(可以假设区间小于10就不再递归)

代码:

void QuickSort(int *a,int begin,int end)
{
	if(begin>end)
	{
		return ;
	}
	if(end - begin>10)
	{
		//快排
	}
	else
	{
		//其他几种排序算法
	}
			
}

4、🌈最终优化代码(没有加上小区间处理)

void GetMidIndex(int*a,int begin,int end)
{
	int mid=(begin+end)/2;
	if(a[begin]<a[end])
	{	
		if(a[mid]>a[end])
		return end;
		else if(a[mid]<a[begin])
		return begin;
		else
		return mid;
	}
	else
	{
		if(a[mid]>begin)
		return begin;
		else if(a[mid]<a[end])
		return end;
		else
		return mid;
	}
	//找到了之后和begin交换以下即可作为keyi
}

int PartSort(int *a,int begin,int end)
{
	int prev,cur,key;
	prev=key=begin;
	cur=begin+1;
	int midi=GetMidIndex(a,begin,end);
	Swap(&a[midi],&a[begin]);
	while(cur<=end)
	{
		if(a[cur]<a[key])
		{
			++prev;
			Swap(&a[cur],&a[prev]);
		}
		++cur;
	}
	Swap(&a[prev],&a[key]);
	return prev;
}

void QuickSort(int *a,int begin,int end)
{
	if(begin>=end)
	return ;
	int keyi=PartSort(a,begin,end);
	QuickSort(a,begin,keyi-1);
	QuickSort(a,keyi+1,end);
}

五、⚽快速排序非递归

递归的大问题就是,极端场景下,深度太深,会栈溢出
1.直接改循环,比如斐void波那契数列、归并排序
2.用数据结构栈模拟递归过程

代码:

void QuickSortNonR(int*a,int begin,int end)
{
	ST st;
	StackInit(&st);
	StackPush(&st,end);
	StackPush(&st,begin);

	while(!StackEmpty(&st))
	{
		int left = StackTop(&st);
		StacckPop(&st);
		int right = StackTop(&st);
		StacckPop(&st);
		
		int keyi=PartSort(a,left,right);
		if(right>keyi+1)
		{
			StackPush(&st,right);
			StackPush(&st,keyi+1);
		}
		if(left<keyi-1)
		{
			StackPush(&st,keyi-1);
			StackPush(&st,left);
		}
		
	}
	StackDestroy(&st);
}

用队列也能完成,用队列的话就是以层序遍历的方式进行,模拟不出函数调用堆栈的实际情况
只是访问的顺序不一样,要保持需要的那个顺序,那就要用栈

七、⚽总结

在八大排序算法中,快速排序是非常快的也是非常重要的,但是普通的快速排序还没有那么快,快的是优化过后的快排
在这里插入图片描述
加粗样式

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猪皮兄弟

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值