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; }