快速排序算法由于它的方便和高效在实践中一直得到广泛的应用,是排序算法中非常值得学习和掌握的一种。
思想
设定被排序的数组是A,快速排序算法的基本思想是用数组的首元素作为标准将A划分成前、后两个部分,比首元素小的元素构成数组的前部分,比首元素大的元素构成数组的后部分。这两个部分构成两个新的子问题,算法接着分别对这两部分递归地进行排序。算法的关键在于怎样划分数组A而将其归约成两个子问题。这里用到了分治的算法思想。
先从后向前扫描数组A,找到第一个不大于A[p]的元素A[j],然后再从前往后扫描数组A,找到第一个不小于A[p]的元素A[i],当i<j时,交换A[i]与A[j]的值。这时候A[j]后面的元素都大于A[p],A[i]前边的数都小于A[p]。然后继续进行扫描,直到i>j时,就岱庙了A[p]在排好序的数组中的正确位置q,然后将A[p]与A[q]的值交换,一次排序就完成了。然后将位置q左右的数组元素,分成两个部分,再次递归调用排序算法。
代码
void QuickSort(int numArr[], int p, int r)
{
int q;
if(p<r)
{
q = Partition(numArr,p,r);
QuickSort(numArr,p,q-1);
QuickSort(numArr,q+1,r);
}
}
int Partition(int numArr[],int p, int r)
{
int x = numArr[p];
int i = p;
int j = r;
while(1)
{
while(numArr[j]>x)
j--;
while(numArr[i]<x)
i++;
if(i<j)
{
int temp = numArr[i];
numArr[i] = numArr[j];
numArr[j] = temp;
}
else
return j;
}
}
分析
下面对这个算法进行实践复杂度分析,首先说明划分过程的工作量是O(n),因为每个元素都需要和首元素进行1次比较。除了这个工作量外,就是两个子问题递归调用的工作量。在最坏的情况下,比如说逆序排列,那么子问题的规模每次都接近于当前的数组大小,那么它的递推方程就成为T(n) = T(n-1) + O(n),也就是说,它的时间复杂度为O(n^2)。
在一般的情况下,每一个子问题的规模都接近于n/2,那么在这样的情况下,递推方程就是T(n) = 2T(n/2) + O(n),根据主定理,可以得知它的时间复杂度为O(nlogn)。这是快速排序最好的情况。