快速排序

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章