前言
- 怎么说呢,快排一直是我的伤痛,我忘不了某晚凌晨两点用纸笔跑代码的辛酸。
- 比起冒泡和选择这些排序界的弟弟,快排还是融合了很多知识点的。
- 在彻底掌握之后,俺还是想把这玩意记录一下。
目录
一、初探快排
快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 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 快速排序https://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遇到了多少困难,但是好盆友就是要双向奔赴的呀,要手拉手肩并肩通力合作,这样才能战胜一个又一个困难,在一次又一次的挫折中锤炼出最真挚的感情,啧,我简直是人生导师~
那快排就说到这里了喔~下周再见