快速排序算法详解

来源:互联网 发布:淘宝网皮衣女装 编辑:程序博客网 时间:2024/04/30 04:28

目标:将数组按从小到大排序

算法思想:快速排序的基本思想是基于分治法的。分治法是什么呢?就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。(没错就是百度来的)

算法描述:在待排序数组中任取一个元素pivot作为枢轴,通过一趟排序,将待排序数组划分为两个部分,A[1...k-1]和A[k+1...n],使得A[1...k-1]中的元素都小于pivot,A[k+1...n]中的元素都大于或等于pivot,而pivot则放在最终位置k上,再分别递归地对两个子数组重复以上过程,直至每个部分内都只有一个元素或者为空为止。


算法实现(已经过测试运行):

void QuickSort(int A[],int low,int high){if(low<high) //递归出口{int local=Partition(A,low,high);  QuickSort(A,low,local-1);QuickSort(A,local+1,high);}}/*函数功能:对数组进行划分,使选中的枢轴pivot到达最终位置local,数组左边的都小于pivot,数组右边的都大于pivot**返回值  :返回值为int,是枢轴的最终位置local**函数参数:待处理子数组以及其左右边界*/int Partition(int A[], int low, int high){int pivot=A[low]; //将当前表中的第一个数设置为枢轴(可选其他的设置方式)while(low<high){while(low<high && A[high]>=pivot) high--;  //从后向前查找比枢轴小的元素,并移动到枢轴前面A[low]=A[high];while(low<high && A[low]<=pivot) low++;  //从前向后查找比枢轴大的元素,并移动到枢轴后面A[high]=A[low];}A[low]=pivot; //将枢轴放到最终位置return low;}


一趟排序过程详解:
数组初始状态:6   2   7   3   9,选取6为枢轴
第一步:high所指元素为9,比pivot=6大,high前移


第二步:high所指元素为3,比pivot=6小,交换low和high所指元素

交换之后:



第三步:发生了一次交换,所以现在转向从low开始向后找。low所指元素为3,比pivot=6小,low后移


第四步:low所指元素为2,比pivot=6小,low后移


第五步:low所指元素为7,比pivot=6大,交换low和high所指元素
交换后:


第六步:发生了一次交换,所以现在转向从high开始向前找。high所指元素为7,比pivot=6大,high前移。前移之后,low和high相等,循环结束。




算法性能
时间复杂度

最好情况,每次选的枢轴正好划分出两个长度相等或者长度差1的子数组。这种情况下的时间复杂度为O(nlogn)。这个是怎么算出来的呢?快速排序的运算次数为,划分操作的运算次数n 加上对两个子序列进行快速排序的运算次数,即T(n)=2*T(n/2)+n,然后计算出T(n),这就是一个数学问题了。。。。
最坏情况,没次选的枢轴,恰好恰好划分出一个长度为0的子数组,即两个子数组最不对称。比如一个本身就是按从小到大排列的数组。此时时间复杂度为O(n2)
平均情况O(nlogn)


空间复杂度
快速排序要借助一个递归工作栈,栈的容量应该与递归调用的最大深度保持一致。
最好情况,O(logn)
最坏情况,O(n)
平均情况,O(logn)


改进
(选枢轴)如果数组本身有序,那么,每次选取数组的第一个元素作为枢轴势必会导致最坏的情况出现。所以需要尽力量选取一个可以将数组中分的枢轴。可以从数组的头、尾、中间取三个元素,再选这三个元素的中间值作为最终枢轴。或者在数组中随机选取枢轴。这样使得最坏的情况在实际排序中几乎不会发生。


(三分区)当要分区的所有的元素值都相等时,一般的快速排序算法就陷入了最坏的一种情况,也即反复的交换相同的元素并返回最差的中轴值。无论是任何数据集,只要它们中包含了很多相同的元素的话,这都是一个严重的问题,因为许多“底层”的分区都会变得完全一样。对于这种情况的一种改进办法就是将分区分为三块而不是原来的两块:一块是小于中轴值的所有元素,一块是等于中轴值的所有元素,另一块是大于中轴值的所有元素。另一种简单的改进方法是,当分区完成后,如果发现最左和最右两个元素值相等的话就避免递归调用而采用其他的排序算法来完成。


(大规模下先快排,小规模下用堆排序)快速排序对于大规模数据集处理能力较强,但是对小规模数据集的性能并不好。因此,当递归得到的子序列规模较小时,可采用对小规模数据集处理能力较强的堆排序。


(先快排,几乎有序之后用直接插入排序)另一种优化改进是当分区的规模达到一定小时,便停止快速排序算法。也即快速排序算法的最终产物是一个“几乎”排序完成的有序数列。数列中有部分元素并没有排到最终的有序序列的位置上,但是这种元素并不多。可以对这种“几乎”完成排序的数列使用插入排序算法进行排序以最终完成整个排序过程。因为插入排序对于这种“几乎”完成的排序数列有着接近线性的复杂度。

0 0
原创粉丝点击