本文主要讲解一下排序方法:程序员
如下代码中数组都是从下标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; } 复制代码