排序算法之快速排序
快速排序的思想:
分治法,将大问题分为若干个小的问题,解决小问题然后合成大问题的解
典型的快速排序的一般过程:
1、在数组中找到一个数,一般选作数组最后一个数作为中轴数X
2、以中轴数X作为中心,使用一次划分partition,使得中轴数左边的数都比X小,右边的数都比X大,换句话说经历过一次划分后,中轴数X处在它本来应该在的位置上
3、递归调用partition,最后使得整个数组有序
一次划分partition过程:
1 int partition(int data[],int start,int end){ 2 int i=start-1; 3 int j=start; 4 int x=data[end]; 5 for(;j<end;j++){ 6 if(data[j]<x){ 7 ++i; 8 exchange(data+i,data+j); 9 } 10 } 11 exchange(data+i+1,data+end); 12 return i+1; //i标识的是数组中值小于X的最大下标,i+1即指一次划分后X所在的下标位置 13 }
递归调用的过程:
1 void quicksort(int data[],int start,int end){ 2 if(start<end){ 3 int index=partition(data,start,end); 4 quicksort(data,start,index-1); 5 quicksort(data,index+1,end); 6 } 7 }
注:函数的边界检查条件可以交付给上层调用函数来完成
快速排序的时间复杂度分析:
上述为了简便说明,总是选取待排序数组的最后一个元素作为中轴数,假设一种极端的情况,待排数组已经有序或者元素值全部相同,进行一次partition划分后,中轴数左右两边的元素个数分别是N-1,0,即每经过一次划分,就仅仅将一个数组元素放置在它应该在的位置上,剩余的N-1个数还需要继续排序,每次划分的时间复杂度为遍历一次待排数组所消耗的时间O(n)
设整个排序的时间复杂度为T(n),则最坏的情况下,T(n)=T(n-1)+T(0)+O(n),可以递推得T(n)=O(n^2)
算法的最佳时间复杂度,在最理想的情况下,每次划分总是将待排数组均衡划分,即一次划分完成后,中轴数左右两边的元素个数都是(N-1)/2,则其总的时间复杂度
T(n)<=2*T(n/2)+O(n),T(n)=O(nlgn)
算法的平均时间复杂度,T(n)=O(nlgn),(任何一种按常数比例进行的划分都会产生深度为O(lgn)的递归树,每一层的时间复杂度为进行一次划分的时间O(n),所以总的时间复杂度是O(nlgn)----算法导论),可以理解为,如果某一层的划分效果比较差,则该层下面的划分可能会比较好,好差划分随机的分布在递归树的各层,总的划分渐进于每层都是情况好的划分。
快速排序的稳定性:
由快排的三个过程可以知道,快排不是一种稳定的排序,例如[2,5,6,4(1),7,3,8,4(2)],经历一次划分后,数组变为[2,3,4(2),4(1),7,5,8,6],括号标识第1和第2个4.
快速排序的改进:
快速排序对于输入数组的随机性有要求,如果待排序的数组元素具有较大的随机性,则排序效果接近于基于比较的排序方法的下限O(nlgn),如果输入的数组基本有序,则不适用于快速排序。
因此,对于待排序数组,可以给出快排的随机化版本,每次在划分之前,在数组中随机选择一个数T和数组中最后一个数X交换位置,交换过后调用上述partition函数,从而将随机选择的元素T作为中轴数,从而优化排序的性能。
参考资料:《算法导论》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?