快速排序的究极奥义



前言

  • 怎么说呢,快排一直是我的伤痛,我忘不了某晚凌晨两点用纸笔跑代码的辛酸。
  • 比起冒泡和选择这些排序界的弟弟,快排还是融合了很多知识点的。
  • 在彻底掌握之后,俺还是想把这玩意记录一下。

目录

前言

一、初探快排

二、拿下快排

1.思想解读

2.代码展示

总结



一、初探快排

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。

快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀,在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案:

快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。

 本段取自菜鸟教程:1.6 快速排序icon-default.png?t=LA92https://www.runoob.com/w3cnote/quick-sort-2.html


二、拿下快排


1.思想解读

  • 图解

 你可能会吐槽这是个什么玩意(有一说一这破图看起来真费劲)

还是得能说会道的我给大家讲解一下~


1.文字叙述

简述:

1. 在数组中选一个数字作为基准数(通常为第一个数);

2. 将数组中小于基准数的数移到基准数左边,大于基准数的移到基准数右边;

3. 对于基准数左、右两边的数组,不断重复以上两个过程。

因为俺是看《啊哈!算法》悟了的,所以这儿咱就用用人家的图,不过分吧?

好的!正文开始!(这个颜色叫“开门红”)

咱先把这俩可爱的小家伙提溜出来。

 好的,正如你所见咱突然就有了十个数“6   1   2   7   9   3   4   5   10   8”,还有俩可爱的小哨兵。

他俩的任务就是通过搬脚底下带数字的砖,把这十个数从小到大排出来,怎么搬呢?这还得咱当个总司令战略部署一下。

咱呢,先把第一个数(也就是6)定位基准数。

j的任务是:找到比基准数小的数字。

i的任务是:找到比基准数大的数字。

然后让哨兵j先朝着i缓缓走去(而且每次都要让j先走!!)。

肯定会有人问:“凭啥先让j先走啊?他长得帅还是咋的?”

我的回答是:漏!

先听我的呗,咱先往下继续走着瞧呗,知道你正能量!还为哨兵i打抱不平了。

 如果j遇到的数字比基准数大,那就让他一步一步往前走(即j++),当j遇到了比6小的数字后,让他立正不要动了!好的,很乖巧的小j!

然后我们让i开始走向j(双向奔赴真的是有磕到!),小i和小j的任务恰恰相反,怎么说呢?如果i遇到的数字比基准数小,那就让他一步一步往前走(即i++),当i遇到了比6大的数字后,让他立正!

 

//交换后的序列如下:
    6  1  2  5  9  3  4  7  10  8


这时候他俩需要交流一下,把脚底下的砖交换一下。

交换完信物之后就重新启程咯!他俩就一直重复上面的方法不断做交换:

第二次:

 

//交换后的序列如下:
    3  1  2  5  4  6  9  7  10  8

直到他俩相遇后,在某一块砖的见证下紧紧相拥~这块砖就有了特殊的意义。

 

所以他们打算把这块砖和基准数所在的小砖块交换一下位置。

他们回望来路,发现i身后的数字都小于基准数,j身后的数字都大于基准数了,这简直是太棒了!

相遇了他们就过上幸福快乐的生活了吗?答案又是单字一个:no!

这才哪到哪呀,只能说他们坎坷友情里第一道关卡顺利通过。在很多很多人际关系里不都这样吗?会有很多相似的事情不断发生,我们要手拉手不厌其烦的解决问题,这样才会让感情向前迈进。正如鲁迅所说:A friend in need is a friend indeed!(鲁迅表示并不愿意背这个锅)

所以我们需要重复上述过程,分别处理左右两个序列。先拿左边的序列“3  1  2  5  4”为例,即将3作为基准数,最终使3左边都小于3,3右边都大于3。

后续过程不做赘述了,跟上面一样一样的。左边处理完用同样的方法处理右边。

甩个图,帮助大家理解一下子。


 事已至此,看来我不得不告诉你为什么让j先走了,难道真的是因为小j超帅吗?我再单字一个:no!

小j委屈88的说:明明可以靠才华,你们偏偏诽谤我靠颜值。


大佬是这么说的:啊哈算法关于快速排序法为什么一定要从右边开始的原因

while(a[j]>=temp&&i<j){
	j--;
}	
while(a[i]<=temp&&i<j){
	i++;
}

 简而言之就是有可能相遇的那个数大于基准数,因为i找的是大于基准数的数字,难免发生这种情况,这时候就不好办了,虽然说是有可能,但是我们做事必须严谨一点!鉴于此,那我们还是从j跑起来比较合理。

 


2.代码展示

#include<stdio.h>
int a[101];
void quicksort(int left,int right)
{
	int i,j,t,temp;
	if(left<right){
		temp=a[left];
		i=left;
		j=right;  
		while(i!=j){
			while(a[j]>=temp&&i<j){
				j--;
			}	
			while(a[i]<=temp&&i<j){
				i++;
			}
			t=a[i];
			a[i]=a[j];
			a[j]=t;
		}
		a[left]=a[i];
		a[i]=temp;
		quicksort(left,i-1);
		quicksort(i+1,right);
	}
}
int main()
{
	int i,n;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	quicksort(1,n);
	for(i=1;i<=n;i++){
		printf("%d ",a[i]);
	}
	return 0;
}


总结

1.快排真的有让人爽到,妈妈问我为什么对着代码学猫叫?

2.你看i和j遇到了多少困难,但是好盆友就是要双向奔赴的呀,要手拉手肩并肩通力合作,这样才能战胜一个又一个困难,在一次又一次的挫折中锤炼出最真挚的感情,啧,我简直是人生导师~

那快排就说到这里了喔~下周再见

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

饮星月

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

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

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

打赏作者

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

抵扣说明:

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

余额充值