本文主要講解一下排序方法:程序員
如下代碼中數組都是從下標1開始的,下標0用做哨兵;排序代碼都是進行升序排列。算法
展現部分公共代碼,瞭解後會對後面代碼的閱讀減小一下障礙。shell
typedef struct { int r[MAXSIZE + 1]; int length; }SqList; void swap(SqList *L, int i, int j) { int temp = L->r[i]; L->r[i] = L->r[j]; L->r[j] = temp; } void print(SqList L) { int i; for (i = 1; i<L.length; i++) { printf("%d,", L.r[i]); } printf("%d", L.r[i]); printf("\n"); } 複製代碼
冒泡排序應該是全部程序員最喜歡的排序方法,由於代碼可讀性很是高。數組
//冒泡排序初級版本 void BubbleSort0(SqList *L) { for (int i = 1; i< L->length; i++) { for (int j = i+1; j<= L->length; j++) { if (L->r[i] > L->r[j]) { swap(L, i, j); } } } } 複製代碼
咱們平時所說的冒泡其實不是正宗的,真正的正宗冒泡實際上是這樣的bash
void BubbleSort(SqList *L) { for (int i = 1; i< L->length; i++) { for (int j = L->length - 1; j>=i; j--) { if (L->r[j] > L->r[j+1]) { swap(L, j, j+1); } } } } 複製代碼
內部for循環時從後往前的順序。其實也沒什麼特別的,也很好理解。markdown
增長一個flag標誌函數
void BubbleSort2(SqList *L) { Status flag = TRUE; for (int i = 1; i< L->length && flag; i++) { flag = FALSE; for (int j = L->length - 1; j>=i; j--) { if (L->r[j] > L->r[j+1]) { swap(L, j, j+1); flag = TRUE; } } } } 複製代碼
其實就是能夠提早跳出循環。優化
void SelectSort(SqList *L) { for (int i = 1; i < L->length; i++) { int min = i; for (int j = i+1; j <= L->length; j++) { if (L->r[min] > L->r[j]) { min = j; } } if (i!=min) { swap(L, i, min); } } } 複製代碼
也比較簡單,邏輯上和冒泡有點區別,先找到最小或者最大值,記錄下來,內部循環接受後,與外部循環的i進行交換。spa
void InsertSort(SqList *L) { int temp = 0; for (int i = 2; i<=L->length; i++) { if (L->r[i]<L->r[i-1]) { temp = L->r[i]; int j; for (j=i-1; L->r[j]>temp; j--) { L->r[j+1]=L->r[j]; } L->r[j+1]=temp; } } } 複製代碼
數組從前到後遍歷中,當前元素小於它前面的元素(前面全部的元素已經排序完成),將當前元素出入到前面的已排完的順序中。code
void shellSort(SqList *L) { int increment = L->length; do { increment = increment/3+1; for (int i = increment+1; i<=L->length; i++) { if (L->r[i] < L->r[i-increment]) { L->r[0] = L->r[i]; int j; for (j = i-increment; j > 0 && L->r[0]<L->r[j]; j-=increment) { L->r[j+increment] = L->r[j]; } L->r[j+increment] = L->r[0]; } } } while (increment > 1); } 複製代碼
希爾排序的特色是修改了比較元素之間的步長距離,以前的排序都是臨近的兩個元素進行比較,而希爾排序是可讓編寫者根據需求自行修改比較步長。這樣有一個優勢,根據步長的不一樣與業務需求,會獲得不一樣的時間複雜度。
堆是具備下⾯性質的徹底⼆叉樹:每一個結點的值都⼤於或等於其左右孩⼦結點的值,稱爲⼤頂堆;如圖1;或者每一個結點的值都⼩於等於其左右孩⼦的結點的值,稱爲⼩頂堆,如圖2.
堆排序(Heap Sort) 就是利⽤堆(假設咱們選擇⼤頂堆)進⾏排序的算法.它的基本思想:
//大頂堆調整函數 void HeapAdjust(SqList *L, int s, int m) { int temp = L->r[s]; for (int j = 2*s; j <= m; j*=2) { if (j < m && L->r[j] < L->r[j+1]) { ++j; } if (temp >= L->r[j]) { break; } L->r[s] = L->r[j]; s = j; } L->r[s] = temp; } //堆排序 void HeapSort(SqList *L) { for (int i=L->length/2; i>0; i--) { HeapAdjust(L, i, L->length); } for (int i = L->length; i>1; i--) { swap(L, 1, i); HeapAdjust(L, 1, i-1); } } 複製代碼
#define N 9 int main(int argc, const char * argv[]) { // insert code here... printf("Hello, 排序算法\n"); int i; int d[N]={9,1,5,8,3,7,4,6,2}; //int d[N]={9,8,7,6,5,4,3,2,1}; //int d[N]={50,10,90,30,70,40,80,60,20}; SqList l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10; for(i=0;i<N;i++) l0.r[i+1]=d[i]; l0.length=N; l1=l2=l3=l4=l5=l6=l7=l8=l9=l10=l0; printf("排序前:\n"); print(l0); printf("\n"); //1.初級冒泡排序 printf("初級冒泡排序:\n"); BubbleSort0(&l0); print(l0); printf("\n"); //2.冒泡排序 printf("冒泡排序:\n"); BubbleSort(&l1); print(l1); printf("\n"); //3.冒泡排序優化 printf("冒泡排序(優化):\n"); BubbleSort2(&l2); print(l2); printf("\n"); //4.選擇排序 printf("選擇排序:\n"); SelectSort(&l3); print(l3); printf("\n"); //5.直接插入排序 printf("直接插入排序:\n"); InsertSort(&l4); print(l4); printf("\n"); //6.希爾排序 printf("希爾排序:\n"); shellSort(&l5); print(l5); printf("\n"); //7.堆排序 //注意:執行對排序時更換一下數據源. 這裏我爲何要這組數據,緣由是爲了與下標個位數字講解時進行區分;由於在這個算法講解過程,出現了不少下標的相關計算. /* int d[N]={50,10,90,30,70,40,80,60,20}; */ printf("堆排序:\n"); HeapSort(&l6); print(l6); printf("\n"); printf("\n"); return 0; } 複製代碼