手把手教你快速排序(递归)

众所周知,选择、冒泡排序的时间复杂度是O(n^2),而所谓的快速排序,时间复杂度只有O(n*logn),那什么是快速排序呢,下面就跟着小编一起来探究吧!(点赞更新非递归方法哦😬

封面选的好,绅士少不了😏

目录

一.排序原理

二.代码实现

三.拓展


一.排序原理

假设有一串无规律的数字,现在需要我们把它从小到大依次排好,根据快速排序的原则,我们可以这样进行:

1.随便找出一个数a,比它小的在它左边大的在它右边,不管是左边还是右边都不需要有序排好。

2.在a的左边再找一个数b,a的右边找一个数c,b和c重复1.的步骤(分治、递归)

上图一眼懂:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCx6KaBIOWuheWcqOWutg==,size_20,color_FFFFFF,t_70,g_se,x_16

二.代码实现

快速排序的代码可以分为两个大部分,一是找出一个数把大的放左边小的放右边,二是把一步骤进行递归直到只剩一个数。

一步骤我们可以用挖坑法来解决,选择数组中最左边的值做坑位(你定义的那个中间值)。定义两个变量,一个指向数组中坑位的下一个下标left,一个指向数组尾部下标right。先进行right与坑位的对比,如果right的话就与坑位交换,坑位指向right,right再减1;之后进行left与坑位的比较,left的话就与坑位交换,坑位指向left,left加1。之后再进行right的比较,left的比较,直到left>=right为止

二步骤就是在一步骤结束后进行分治递归但数组要被拆分成两部分,一部分是数组开头到坑位(你定义的数字最终在的下标),即坑位左边;另一部分是坑位的右边。递归的结束条件就是数组只剩下一个数为止。

上代码:

void quickSort(int* a, int left,int right)//快速排序
{
	if (left >= right) return;//递归返回条件,只剩下一个数值
	int pivot=left,begin=left,end=right, key = a[pivot];//pivot:坑 , key:所选择的数值
	while (begin < end)//选择一个数值key,左边放小于其的,右边放大于其的
	{
		while (begin < end && a[end]>=key)//两个条件要同时满足,若只有后者则会出现end 小于begin了,却把小数放在右边的情况
		{
			end--;
		}
		a[pivot] = a[end];//把小的数放入坑内
		pivot = end;//坑的位置变成了key数值右边的
		while (begin < end && a[begin] <= key)
		{
			begin++;
		}
		a[pivot] = a[begin];
		pivot = begin;
	}
	a[pivot] = key;//把key放入最终的坑中
	quickSort(a, left, pivot - 1);//递归最终key左边的
	quickSort(a, pivot + 1, right);//递归最终key右边的
}

三.拓展

(一)我们在实现每一次排序时除了用挖坑法还可以用前后指针法,实现方法如下:

1.选择左边的数做中间数key,定义两个变量(front,back)同时指向最左边的下一位,进行front与key的比较,如果小就进行front与back的交换,然后都加一;如果大的话就front加一,直到front走到数尾为止。

代码献上:

void quickSort_fnb(int* a, int left, int right)//前后法快排
{
	if (left >= right) return;
	int front = left + 1, back = left + 1;
	while (front <= right)
	{
		while (front <= right && a[front] > a[left]) front++;
		if (front <= right)
		{
			swap(&a[back], &a[front]);
			back++;
			front++;
		}
	}
	swap(&a[left], &a[back - 1]);
	quickSort_fnb(a, left, back - 2);
	quickSort_fnb(a, back, right);
}

(二).细节优化,三数取中,确保复杂度O(n*logn)

细心的朋友已经发现了,我们每一次选择数字的时候都只能选择最左边的数,那这样就有可能造成选择数字过大或过小举个例子:987654321。我们每次选择的时候最坏一次只能排序一个数字,这样时间复杂度是O(n^2)了!。那么我们该怎么优化呢,通用的做法是选择这个数组中的中间下标数,让它与最左边和最右边的数进行比较,找出三个数中的中间值,与最左边的数交换即可。

代码来了:

int midnum(int* a, int left, int right)//三数取中
	int mid = (left + right) >> 1;
	if (a[left] < a[mid])
	{
		if (a[right] > a[mid]) return mid;
		else if (a[right] < a[left]) return left;
		else return right;
	}
	else // a[left]>a[mid]
	{
		if (a[mid] > a[right]) return mid;
		else if (a[right] > a[left]) return left;
		else return right;
	}
	return mid;
}

创作不易 多多点赞支持😆, 如有错误,敬请斧正

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

就要 宅在家

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

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

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

打赏作者

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

抵扣说明:

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

余额充值