希爾排序相當於直接插入排序的升級,它們同屬於插入排序類,堆排序相當於簡單選擇排序的升級,它們同屬於選擇排序類。而快速排序其實就是我們前面認爲最慢的冒泡排序的升級,它們都屬於交換排序類。即它也是通過不斷的比較和移動交換來實現排序的,只不過它的實現,增大了記錄的比較和移動的距離,將關鍵字較大的記錄從前面直接移動到後面,關鍵字較小的記錄從後面直接移動到前面,從而減少了總的比較次數和移動交換次數。
快速排序(Quick Sort)的基本思想是:通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。
以前總覺得快速排序挺晦澀的,大學的時候考試還是得死記硬背呢!今天看到了伍迷老師對各種排序算法的講解,覺得思路清晰,十分易懂,以後應該不會忘記的乾乾淨淨了。放在這裏爲學習備份~~
下面看這個思想十分容易接受的程序:
#include <stdio.h> void swap(int *a,int *b) { int temp; temp = *a; *a = *b; *b = temp; } /* 交換a中子表的記錄,使樞軸記錄到位,並返回其所在位置 */ /* 此時在它之前(後)的記錄均不大(小)於它。 */ int partition(int a[],int low,int high) { int pivotKey; pivotKey = a[low];//用子表的第一個記錄作樞軸記錄 while(low < high)// 從表的兩端交替地向中間掃描 { while(low < high && a[high] >= pivotKey) high--; swap(&a[low],&a[high]);//將比樞軸記錄小的記錄交換到低端 while(low < high && a[low] <= pivotKey) low++; swap(&a[low],&a[high]);//將比樞軸記錄大的記錄交換到高端 } return low;//返回樞軸所在位置 } void QSort(int a[],int low,int high) { int pivot; if(low < high) { pivot = partition(a,low,high);//將a數組一分爲二,且返回樞值的位置 QSort(a,low,pivot-1);//對低子表遞歸 QSort(a,pivot+1,high);//對高子表遞歸 } } //快速排序 void QuickSort(int a[],int n) { QSort(a,0,n-1);//由於需要遞歸調用,因此我們外封裝了一個函數 } void main() { int i; int a[8] = {5,4,1,3,9,5,8,2}; QuickSort(a,8); for(i=0;i<8;i++) { printf("%d ",a[i]); } printf("\n"); }
因爲在現實中,待排序的系列極有可能是基本有序的,此時,總是固定選取第一個關鍵字(其實無論是固定選取哪一個位置的關鍵字)作爲首個樞軸就變成了極爲不合理的作法。
改進,於是就有了三數取中(median-of-three)法。即取三個關鍵字先進行排序,將中間數作爲樞軸,一般是取左端、右端和中間三個數,也可以隨機選取。這樣至少這個中間數一定不會是最小或者最大的數,從概率來說,取三個數均爲最小或最大數的可能性是微乎其微的,因此中間數位於較爲中間的值的可能性就大大提高了。由於整個序列是無序狀態,隨機選取三個數和從左中右端取三個數其實是一回事,而且隨機數生成器本身還會帶來時間上的開銷,因此隨機生成不予考慮。我們來看看取左端、右端和中間三個數的實現代碼,在Partition函數代碼的第3行與第4行之間增加這樣一段代碼。
int pivotkey; int m = low + (high - low) /* 計算數組中間的元素的下標 */ if (a[low]>a[high]) swap(&a[low],&a[high]); /* 交換左端與右端數據,保證左端較小 */ if (a[m]>a[high]) swap(&a[high],&a[m]); /* 交換中間與右端數據,保證中間較小 */ if (a[m]>a[low]) swap(&a[m],&a[low]); /* 交換中間與左端數據,保證左端較小 */ /* 此時L.r[low]已經爲整個序列左中右三個關鍵字的中間值。*/ pivotkey=L->r[low]; /* 用子表的第一個記錄作樞軸記錄 */