快速排序
用于排序的最佳的实用选择,因其平均性能很好,期望运行时间O(nlog2^n),最坏情况运行时间为O(n^2).
1、快速排序
和合并排序一样,采取分治模式:
(1)分解:数组A[p..r]被划分为两个(可能为空)子数组A[p..q-1]和A[q+1..r],使得A[p..q-1]中的每个元素都小于等于A[q],而A[q+1..r]大于A[q],q在此划分过程中进行计算。
(2)解决:递归调用快速排序,对子数组A[p..q-1]和A[q+1..r]排序
(3)合并:两个子数组都是原地排序的,因此合并不需要操作,整个数组就已经排好序
伪代码: QUICKSORT(A,p,r) if p<r then q<—PARTITION(A,p,r) QUICKSORT(A,p,q-1) QUICKSORT(A,q+1,r)
最初调用QUICKSORT(A,1,A.length).
其中子程序PARTITION(A,p,r)代码如下,实现对子数组A[p..r]进行重排:
//对数组A[p,...,r]进行重排,使得A[p..q-1]中的每个元素都小于等于A[q],而A[q+1..r]大于A[q] int partition(int a[],int p,int r){ int key=a[r]; int index=p-1; int temp=0; for(int i=p;i<=r-1;i++){ if(a[i]>key){ index++; temp=a[i]; a[i]=a[index]; a[index]=temp; } } temp=a[index+1]; a[index+1]=a[r]; a[r]=temp; return index+1; }
将A[r]=key作为参照数来进行对比,最后的结果是A[p..q-1]中的每个元素都小于等于key,而A[q+1..r]大于key,而A[q]最后和A[r]交换,并返回q。此子程序的运行时间为O(n),其中n=r-p+1.
2、快速排序的性能
快速排序的运行时间和划分是否对称有关,而后者又和选择了哪一个元素来进行划分比较有关。如果划分对称,渐近意义上,与合并算法(nlog2^n)一样快,不对称时,渐近意义上,和插入算法(n^2)一样慢。
(1)最坏情况
最坏划分是划分过程中产生的两个区域分别包含了n-1个元素和1个0元素。假设每次递归调用都出现了这种划分,划分的时间代价是O(n),递归树有n层,则算法的运行时间为O(n^2).递归表达式T(n)=T(n-1)+O(n).
(2)最好情况
最平衡的划分,两个子数组的大小都不可能大于n/2,递归式T(n)<=2T(n/2)+O(n),解为O(nlog2^n).由于每一层递归的划分都是两边对称的,因此,从渐进意义上,算法运行就更快了。
(3)平衡划分
平均情况和最佳运行时间很接近
任何一种按常数比例进行的划分都会产生深度为O(log2^n)的递归树,其中每一层的代价为O(n),因而,当按照常数比例划分时,总运行时间是O(nlog2^n).
3、快速排序的随机化版本
加入随机化部分,以便对于所有的输入,它均能获得较好的平均情况性能。随机化算法使得输入数组不相关,使最坏情况发生的可能性变小。
伪代码 RANDOMIZED-PARTITION(A,p,r) i<—Random(p,r) exchange A[r]<->A[i] return PARTITION(A,p,r)
4、Hoare划分
将数组第一个元素作为对比元素,即主元,用两个指针分别从数组的首尾进行遍历,首指针找到第一个比主元大的元素,尾指针找到第一个比主元小(包括等于)的元素,将两个元素交换,循环进行此过程指导首尾指针相遇,最后的尾指针(包括尾指针)之前是比主元小(等于)的元素,尾指针后是比主元素大的元素,将尾指针位置的元素和主元素交换,并返回尾指针位置。
伪代码: HOARE-PARTITION(A,p,r) x<—A[p] i<—p-1 j<—r+1 while TRUE do repeat j<—j-1 until A[j]<=x repeat i<—i+1 until A[i]>x if i<j then exchange A[i]<—>A[j] else A[p]<—>A[j] return j c代码 int hoare_partition(int a[],int p,int r){ int temp=0; int key=a[p]; int i=p-1; int j=r+1; while(true){ do{ j--; }while(a[j]>key); do{ i++; }while(a[i]<=key); if(i<j){ temp=a[i]; a[i]=a[j]; a[j]=temp; } else{ temp=a[p]; a[p]=a[j]; a[j]=temp; return j; } } }
此划分算法的运行时间为O(n),n=r-p+1.