1、概念

快速排序(Quick Sort)的基本思想是:通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录关键字小,则可分别对这两部分记录继续进行排序,已到达整个序列有序的目的。

2、算法介绍

设要排序数组是A[0]……A[N-1],首先任意选取一个数据(通常选用中间的数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

一趟快速排序的算法是:

1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;

2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];

3)从j开始向前搜索,即由后开始向前搜索(j -- ),找到第一个小于key的值A[j],A[i]与A[j]交换;

4)从i开始向后搜索,即由前开始向后搜索(i ++ ),找到第一个大于key的A[i],A[i]与A[j]交换;

5)重复第3、4、5步,直到 I=J; (3,4步是在程序中没找到时候j=j-1,i=i+1,直至找到为止。找到并交换的时候i, j指针位置不变。另外当i=j这过程一定正好是i+或j-完成的最后令循环结束。

3、code

/*对顺序表L作快速排序*/
void QSort(SqList *L,int low,int high)
{
    int pivot;
    if(low<high)
    {
        pivot=Partition(L,low,high);    /*将L->r[low…high]一分为二,算出枢轴pivot*/
        QSort(L,low,pivot-1);
        QSort(L,pivot+1,hight);
    }
}

/*交换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置*/
/*此时在它之前(后)的记录均不大(小)于它*/
/*把L->r[low]放在合适的位置,并返回这个位置*/
int Partition(SqList *L,int low,int high)
{
    int pivotkey;
    pivotkey=L->[low];    /*用于表的第一个记录作枢轴记录*/
    while(low<high)    /*从标的两端交替向中间扫描*/
    {
        while(low<high && L->r[high]>=pivotkey)
            high--;
        swap(L,low,high);    /*将此枢轴记录小的记录交换到低端*/
        while(low<high && ->r[low]<=pivotkey)
            low++;
        swap(L,low,high);    /*将此枢轴记录大的记录交换到高端*/
    }
    return low;    /*返回枢轴所在位置*/
}

4、快速排序优化

(1)优化选取枢轴

如果选取的pivotkey处于整个序列中间位置,则整个排序的效率较高,但是如果是最小或最大的,则整个排序效率较低。

优化方案:三数取中法(median-of-three),即取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数。

(2)优化不必要的交换

(3)优化小数组时的排序方案

快速排序适合对非常大的数组的排序,而对于规模小的数组快速排序反而不如直接插入排序更好

(4)优化递归操作

递归对性能有一定的影响。QSort函数在其尾部有两次递归操作,如果待排序的序列极度不平衡,递归深度将趋近于n,而不是平衡时的㏒2n,不仅影响速度,还耗费空间。

code

/*优化小数组时的排序方案:6、15、16行*/
/*优化递归操作:8、12行*/
void QSort(SqList *L,int low,int high) 
{ 
    int pivot;
    if((high-low)>MAX_LENGTH_INSERT_SORT)
    {
        while(low<high) 
        { 
            pivot=Partition(L,low,high);
            QSort(L,low,pivot-1);    /*对低子表递归排序*/
            low=pivot+1;    /*尾递归*/
        }
    }
    else    /*当high-low小于等于常数时用直接插入排序*/
        InsertSort(L);
} 

/*优化选取枢轴:25-31行*/
/*优化不必要的交换:40、43、45行*/
int Partition(SqList *L,int low,int high)
{
    int pivotkey;

    int m=low+(high-low)/2;    /*计算数组中间的元素下标*/
    if(L->r[low]>L->r[high])
        swap(L,low,high);    /*交换左端与右端数据,保证左端较小*/
    if(L->r[m]>r[high])
        swap(L,hig,m);    /*交换中间与右端数据,保证中间较小*/
    if(L->r[m]>L->r[low])
        swap(L,m,low);    /*交换中间与左端数据,保证左端较小*/
    /*此时L.r[low]已成为整个序列左中右三个关键字的中间值*/

    pivotkey=L->[low];
    L->r[0]=pivotkey;    /*将枢轴关键字备份到L->r[0]*/
    while(low<high)
    {
        while(low<high && L->r[high]>=pivotkey)
            high--;
        L->r[low]=L->r[high];    /*采用替换而不是交换的方式进行操作*/
        while(low<high && ->r[low]<=pivotkey)
            low++;
        L->r[high]=L->r[low];    /*采用替换而不是交换的方式进行操作*/
    }
    L->r[row]=L->r[0];    /*返回枢轴所在位置*/
    return low;
}