【冒泡排序】
1 void Bubble_sort(int A[], int N) 2 { 3 int i, P; 4 int flag; 5 6 for(P = N-1; P >= 0; P--) 7 { 8 flag = 0; 9 for(i = 0; i < P; i++) /* 一趟 */ 10 { 11 if(A[i] > A[i+1]) 12 { 13 swap(&A[i], &A[i+1]); 14 flag = 1; 15 } 16 } 17 if(flag == 0) break; /* 全程无交换 */ 18 } 19 }
【快速排序】
1 int quick_partition(int a[], int left, int right) 2 { 3 int tmp; 4 int mid = (left + right) / 2; 5 6 if(a[left] > a[mid]) 7 swap(&a[left], &a[mid]); 8 if(a[left] > a[right]) 9 swap(&a[left], &a[right]); 10 if(a[mid] > a[right]) 11 swap(&a[mid], &a[right]); 12 swap(&a[left], &a[mid]); 13 14 tmp = a[left]; // 取中位数作为第一个坑 15 16 while(left < right) 17 { 18 while(left < right && a[right] >= tmp) 19 right--; 20 if(left < right) 21 a[left++] = a[right]; // 挖坑right, 填坑left 22 23 while(left < right && a[left] <= tmp) 24 left++; 25 if(left < right) 26 a[right--] = a[left]; // 挖坑left, 填坑right 27 } 28 a[left] = tmp; 29 30 return left; 31 } 32 33 void quick_sort(int a[], int left, int right) 34 { 35 int pivot; 36 if(left < right) 37 { 38 pivot = quick_partition(a, left, right); 39 quick_sort(a, left, pivot-1); 40 quick_sort(a, pivot+1, right); 41 } 42 } 43 44 void Quick_sort(int A[], int N) 45 { 46 quick_sort(A, 0, N-1); 47 }
【直接插入排序】
1 void Insertion_sort(int A[], int N) 2 { 3 int i, P; 4 int tmp; 5 6 for(P = 1; P < N; P++) 7 { 8 tmp = A[P]; /* 摸下一张牌 */ 9 for(i = P; i > 0 && A[i-1] > tmp; i--) 10 { 11 A[i] = A[i-1]; /* 移出空位 */ 12 } 13 14 A[i] = tmp; /* 新牌落位 */ 15 } 16 }
【希尔排序】
希尔排序的实质就是分组插入排序,又称为缩小增量排序。
该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有效的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比直接插入排序有较大提高。
1 void Shell_sort(int A[], int N) 2 { 3 int D, P; 4 int i, tmp; 5 6 //printf("xxx,xxx : "); 7 //for(i = 0; i < N; i++) 8 // printf(i == N-1 ? "%2d " : "%2d ", A[i]); 9 10 for(D = N/2; D > 0; D /= 2) /* 希尔增量序列 */ 11 { 12 for(P = D; P < N; P++) /* 插入排序 */ 13 { 14 tmp = A[P]; 15 // 按照增量序列插入 16 for(i = P; i >= D && A[i-D] > tmp; i -= D) 17 A[i] = A[i-D]; 18 A[i] = tmp; 19 20 //printf("D=%d,P=%d : ", D, P); 21 //for(i = 0; i < N; i++) 22 // printf(i == N-1 ? "%2d " : "%2d ", A[i]); 23 } 24 //printf(" "); 25 } 26 }
逆序序列:10,9,8,7,6,5,4,3,2,1
希尔排序步骤:
按照自定义增量序列进行的希尔排序:
1 // 一趟按照增量D进行的插入排序 2 void shell_insert(int A[], int N, int D) 3 { 4 int i, P; 5 int tmp; 6 7 for(P = D; P < N; P++) 8 { 9 tmp = A[P]; 10 for(i = P; i >= D && A[i-D] > tmp; i -= D) 11 A[i] = A[i-D]; 12 A[i] = tmp; 13 } 14 } 15 16 void Shell_sort(int A[], int N) 17 { 18 int D[] = {9, 5, 3, 1}; // 自定义增量序列 19 int i; 20 for(i = 0; i < sizeof(D)/sizeof(D[0]); i++) 21 shell_insert(A, N, D[i]); 22 }
【简单选择排序】
首先,选出数组中最小的元素,将它与数组中第一个元素进行交换。然后找出次小的元素,并将它与数组的第二个元素进行交换。按照这种方法一直进行下去,直到整个数组排完序。它是通过不断选出剩余元素中最小元素来实现的。
选择排序有一个缺点,它的运行时间对文件中已有序的部分依赖较少。从文件中选出最小元素的每遍操作过程,并没有给出下一遍要找的最小元素的位置的相关信息。因此,该程序对已排好的文件或各元素都相同的文件排序所花的时间与对随机排序的文件排序所花的时间基本相同。
1 void Selection_sort(int A[], int N) 2 { 3 int i, j, tmp; 4 int min; 5 6 for(i = 0; i < N-1; i++) 7 { 8 min = i; 9 for(j = i+1; j < N; j++) 10 { 11 if(A[min] > A[j]) 12 min = j; 13 } 14 if(min != i) 15 { 16 swap(&A[min], &A[i]); 17 } 18 } 19 }
选择排序的另一种写法:
1 int ScanForMin(int A[], int start, int end) 2 { 3 int i, min; 4 min = start; 5 for(i = start+1; i <= end; i++) 6 { 7 if(A[min] > A[i]) 8 min = i; 9 } 10 return min; 11 } 12 13 void Selection_sort_x(int A[], int N) 14 { 15 int i, MinPosition; 16 17 for(i = 0; i < N; i++) 18 { 19 // 从A[i]到A[N-1]中找到最小元素,并将其位置赋给MinPosition 20 MinPosition = ScanForMin(A, i, N-1); 21 // 将未排序部分的最小元素换到有序部分的最后位置 22 swap(&A[i], &A[MinPosition]); 23 } 24 }
从以上的思路中考虑,如何快速找到最小元素,是提高效率的方法。
【堆排序】
1 void perc_down(int a[], int i, int n) // i:下标号从0开始, n:结点总数 2 { 3 int child; 4 int tmp; 5 for(tmp = a[i]; (i+1)*2 <= n; i = child) // (i+1)*2<=n: 至少有一个孩子 6 { 7 child = i * 2 + 1; 8 // child != n-1 : 该结点还有右孩子 9 if((child != n-1) && (a[child+1] > a[child])) 10 child++; 11 12 // 已取得左右儿子中较大的那个 13 if(tmp < a[child]) 14 a[i] = a[child]; 15 else 16 break; 17 } 18 a[i] = tmp; 19 } 20 void Heap_sort(int A[], int N) 21 { 22 int i; 23 24 for(i = N/2-1; i >= 0; i--) /* build heap */ 25 perc_down(A, i, N); 26 27 for(i = N; i >= 2; i--) 28 { 29 swap(&A[0], &A[i-1]); /* delete max */ 30 perc_down(A, 0, i-1); 31 } 32 }
【归并排序】
// 有序子列的归并 // L : 左边起始位置 // R : 右边起始位置 // RightEnd: 右边终点位置 void Merge(int A[], int TmpA[], int L, int R, int RightEnd) { int i; int LeftEnd = R - 1; // 左边终点位置,假设左右两列挨着 int Tmp = L; // 存放结果的数组的初始位置 int NumElements = RightEnd - L + 1; static int iCnt = 1; //printf("Merge<%02d> : L=%2d, R=%2d, RightEnd=%2d : ", iCnt++, L, R, RightEnd); while(L <= LeftEnd && R <= RightEnd) { if(A[L] <= A[R]) TmpA[Tmp++] = A[L++]; else TmpA[Tmp++] = A[R++]; } while(L <= LeftEnd) TmpA[Tmp++] = A[L++]; while(R <= RightEnd) TmpA[Tmp++] = A[R++]; // 把TmpA数组中的数据拷贝到原数组A中,TmpA只做临时空间使用 for(i = 0; i < NumElements; i++, RightEnd--) A[RightEnd] = TmpA[RightEnd]; } void MSort(int A[], int TmpA[], int L, int RightEnd) { int Center; //printf("Msort: L=%d, RightEnd:%d ", L, RightEnd); if(L < RightEnd) { Center = (L + RightEnd) / 2; MSort(A, TmpA, L, Center); MSort(A, TmpA, Center+1, RightEnd); Merge(A, TmpA, L, Center+1, RightEnd); } } void Merge_sort(int A[], int N) { int i; int *TmpA; TmpA = malloc(N * sizeof(int)); if(TmpA != NULL) { MSort(A, TmpA, 0, N-1); free(TmpA); } }
Merge合并两个有序数组的调用次序:
【基数排序】
【PAT练习提交代码】:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 void swap(int *a, int *b) 6 { 7 int tmp; 8 tmp = *a; 9 *a = *b; 10 *b = tmp; 11 } 12 // TODO: 1 13 void Bubble_sort(int A[], int N) 14 { 15 int i, P; 16 int flag; 17 18 for(P = N-1; P >= 0; P--) 19 { 20 flag = 0; 21 for(i = 0; i < P; i++) /* 一趟 */ 22 { 23 if(A[i] > A[i+1]) 24 { 25 swap(&A[i], &A[i+1]); 26 flag = 1; 27 } 28 } 29 if(flag == 0) break; /* 全程无交换 */ 30 } 31 } 32 33 // TODO: 2 34 int quick_partition(int a[], int left, int right) 35 { 36 int tmp; 37 int mid = (left + right) / 2; 38 39 if(a[left] > a[mid]) 40 swap(&a[left], &a[mid]); 41 if(a[left] > a[right]) 42 swap(&a[left], &a[right]); 43 if(a[mid] > a[right]) 44 swap(&a[mid], &a[right]); 45 swap(&a[left], &a[mid]); 46 47 tmp = a[left]; // 取中位数作为第一个坑 48 49 while(left < right) 50 { 51 while(left < right && a[right] >= tmp) 52 right--; 53 if(left < right) 54 a[left++] = a[right]; // 挖坑right, 填坑left 55 56 while(left < right && a[left] <= tmp) 57 left++; 58 if(left < right) 59 a[right++] = a[left]; // 挖坑left, 填坑right 60 } 61 a[left] = tmp; 62 63 return left; 64 } 65 66 void quick_sort(int a[], int left, int right) 67 { 68 int pivot; 69 if(left < right) 70 { 71 pivot = quick_partition(a, left, right); 72 quick_sort(a, left, pivot-1); 73 quick_sort(a, pivot+1, right); 74 } 75 } 76 77 void Quick_sort(int A[], int N) 78 { 79 quick_sort(A, 0, N-1); 80 } 81 82 // TODO: 3 83 void Selection_sort(int A[], int N) 84 { 85 int i, j, min; 86 87 for(i = 0; i < N-1; i++) 88 { 89 min = i; 90 for(j = i+1; j < N; j++) 91 { 92 if(A[min] > A[j]) 93 min = j; 94 } 95 if(min != i) 96 { 97 swap(&A[min], &A[i]); 98 } 99 } 100 } 101 102 int ScanForMin(int A[], int start, int end) 103 { 104 int i, min; 105 min = start; 106 for(i = start+1; i <= end; i++) 107 { 108 if(A[min] > A[i]) 109 min = i; 110 } 111 return min; 112 } 113 114 void Selection_sort_x(int A[], int N) 115 { 116 int i, MinPosition; 117 118 for(i = 0; i < N; i++) 119 { 120 // 从A[i]到A[N-1]中找到最小元素,并将其位置赋给MinPosition 121 MinPosition = ScanForMin(A, i, N-1); 122 // 将未排序部分的最小元素换到有序部分的最后位置 123 swap(&A[i], &A[MinPosition]); 124 } 125 } 126 // TODO: 4 127 // 下标从0开始的堆排序 128 void perc_down(int a[], int i, int n) // i:下标号从0开始, n:结点总数 129 { 130 int child; 131 int tmp; 132 for(tmp = a[i]; (i+1)*2 <= n; i = child) // (i+1)*2<=n: 至少有一个孩子 133 { 134 child = i * 2 + 1; 135 // child != n-1 : 该结点还有右孩子 136 if((child != n-1) && (a[child+1] > a[child])) 137 child++; 138 139 // 已取得左右儿子中较大的那个 140 if(tmp < a[child]) 141 a[i] = a[child]; 142 else 143 break; 144 } 145 a[i] = tmp; 146 } 147 void Heap_sort(int A[], int N) 148 { 149 int i; 150 151 for(i = N/2-1; i >= 0; i--) /* build heap */ 152 perc_down(A, i, N); 153 154 for(i = N; i >= 2; i--) 155 { 156 swap(&A[0], &A[i-1]); /* delete max */ 157 perc_down(A, 0, i-1); 158 } 159 } 160 161 // TODO: 4 162 // 下标从1开始的堆排序 163 void perc_down_1(int a[], int i, int n) // n : 结点总数 164 { 165 int child; 166 int tmp; 167 for(tmp = a[i]; i*2 <= n; i = child) 168 { 169 child = i * 2; 170 // child != n : 即可得该结点还有右孩子 171 if((child != n) && (a[child+1] > a[child])) 172 child++; 173 // 已取得左右儿子中较大的那个 174 if(tmp < a[child]) 175 a[i] = a[child]; 176 else 177 break; 178 } 179 a[i] = tmp; 180 } 181 void Heap_sort_1(int A[], int N) 182 { 183 int i; 184 185 for(i = N/2; i > 0; i--) /* build heap */ 186 perc_down_1(A, i, N); 187 188 /* after build heap */ 189 190 for(i = N; i >= 2; i--) 191 { 192 swap(&A[1], &A[i]); /* delete max */ 193 perc_down_1(A, 1, i-1); 194 } 195 } 196 197 // TODO: 5 198 void Insertion_sort(int A[], int N) 199 { 200 int i, P; 201 int tmp; 202 203 for(P = 1; P < N; P++) 204 { 205 tmp = A[P]; /* 摸下一张牌 */ 206 for(i = P; i > 0 && A[i-1] > tmp; i--) 207 { 208 A[i] = A[i-1]; /* 移出空位 */ 209 } 210 211 A[i] = tmp; /* 新牌落位 */ 212 } 213 } 214 215 // TODO: 6 216 void Shell_sort(int A[], int N) 217 { 218 int D, P; 219 int i, tmp; 220 221 for(D = N/2; D > 0; D /= 2) /* 希尔增量序列 */ 222 { 223 for(P = D; P < N; P++) /* 插入排序 */ 224 { 225 tmp = A[P]; 226 // 按照增量序列插入 227 for(i = P; i >= D && A[i-D] > tmp; i -= D) 228 A[i] = A[i-D]; 229 A[i] = tmp; 230 } 231 } 232 } 233 234 // 一趟按照增量D进行的插入排序 235 void shell_insert(int A[], int N, int D) 236 { 237 int i, P; 238 int tmp; 239 240 for(P = D; P < N; P++) 241 { 242 tmp = A[P]; 243 // 以增量D插入 244 for(i = P; i >= D && A[i-D] > tmp; i -= D) 245 A[i] = A[i-D]; 246 A[i] = tmp; 247 } 248 } 249 250 void Shell_sort_x(int A[], int N) 251 { 252 int D[] = {109, 41, 19, 5, 1}; // 自定义增量序列 253 //int D[] = {9, 5, 3, 1}; 254 int i; 255 for(i = 0; i < sizeof(D)/sizeof(D[0]); i++) 256 shell_insert(A, N, D[i]); 257 } 258 259 // TODO: 7 260 // 有序子列的归并 261 // L : 左边起始位置 262 // R : 右边起始位置 263 // RightEnd: 右边终点位置 264 void Merge(int A[], int TmpA[], int L, int R, int RightEnd) 265 { 266 int i; 267 int LeftEnd = R - 1; // 左边终点位置,假设左右两列挨着 268 int Tmp = L; // 存放结果的数组的初始位置 269 int NumElements = RightEnd - L + 1; 270 271 while(L <= LeftEnd && R <= RightEnd) 272 { 273 if(A[L] <= A[R]) 274 TmpA[Tmp++] = A[L++]; 275 else 276 TmpA[Tmp++] = A[R++]; 277 } 278 while(L <= LeftEnd) 279 TmpA[Tmp++] = A[L++]; 280 while(R <= RightEnd) 281 TmpA[Tmp++] = A[R++]; 282 283 // 把TmpA数组中的数据拷贝到原数组A中,TmpA只做临时空间使用 284 for(i = 0; i < NumElements; i++, RightEnd--) 285 A[RightEnd] = TmpA[RightEnd]; 286 } 287 288 void MSort(int A[], int TmpA[], int L, int RightEnd) 289 { 290 int Center; 291 292 if(L < RightEnd) 293 { 294 Center = (L + RightEnd) / 2; 295 MSort(A, TmpA, L, Center); 296 MSort(A, TmpA, Center+1, RightEnd); 297 Merge(A, TmpA, L, Center+1, RightEnd); 298 } 299 } 300 // 归并排序的递归算法 301 void Merge_sort(int A[], int N) 302 { 303 int *TmpA; 304 TmpA = malloc(N * sizeof(int)); 305 if(TmpA != NULL) 306 { 307 MSort(A, TmpA, 0, N-1); 308 free(TmpA); 309 } 310 } 311 312 // 归并排序的非递归算法 313 void Merge_sort_x(int A[], int N) 314 { 315 316 } 317 318 // TODO: 8 319 void Radix_sort(int A[], int N) 320 { 321 322 } 323 324 /*********************************************************/ 325 int main(void) 326 { 327 int i, N; 328 int A[100000]; 329 330 scanf("%d", &N); 331 for(i = 0; i < N; i++) 332 scanf("%d", &A[i]); 333 334 // Bubble_sort(A, N); 335 Quick_sort(A, N); 336 // Selection_sort(A, N); 337 // Heap_sort(A, N); 338 // Insertion_sort(A, N); 339 // Shell_sort(A, N); 340 // Shell_sort_x(A, N); 341 // Merge_sort(A, N); 342 // Radix_sort(A, N); 343 344 for(i = 0; i < N; i++) 345 printf(i == N-1 ? "%d" : "%d ", A[i]); 346 347 return 0; 348 }
测试结果:
排序算法的比较: