首页 > 代码库 > 《算法导论》(CLRS)第三版 第1、2章总结(二)

《算法导论》(CLRS)第三版 第1、2章总结(二)

5. 归并排序 (Merge Sort)

 1 #include <stdio.h> 2 #include <stdlib.h> 3  4 void print(int arr[], int n) { 5     int i; 6     for (i = 0; i < n; i++) { 7         printf("%d ", arr[i]); 8     } 9     printf("\n");10 }11 12 void merge(int arr[], int s, int n) {13     int* L = (int*)malloc(s * sizeof(int));14     int* R = (int*)malloc((n-s) * sizeof(int));15     int i, j, k, t = n - s;16     for (i = 0; i < s; i++) {17         L[i] = arr[i];18     }19     for (j = 0; i < n; i++, j++) {20         R[j] = arr[i];21     }22     i = j = k = 0;23     while (i < s && j < t) {24         if (L[i] <= R[j]) {25             arr[k++] = L[i++];26         } else {27             arr[k++] = R[j++];28         }29     }30     while (i < s) {31         arr[k++] = L[i++];32     }33     while (j < t) {34         arr[k++] = R[j++];35     }36     free(L), free(R);37 }38 39 void merge_sort(int arr[], int n) {40     if (n > 1) {41         int s = n / 2, t = (n+1) / 2;42         merge_sort(arr, s);43         merge_sort(arr+s, t);44         merge(arr, s, n); // [0, s) & [s, n)45     }46 }47 48 int main() {49     int arr[10] = {1, 4, 2, 8, 5, 7, 9, 3, 0, 6};50     int n = 10;51     merge_sort(arr, n);52     print(arr, n);53     return 0;54 }

上面程序中,merge_sort 的时间复杂度为 O(nlgn), 空间复杂度为 O(nlgn)。
下面给出一种原地归并排序的C语言代码:

 1 #include <stdio.h> 2 #include <stdlib.h> 3  4 void print(int arr[], int n) { 5     int i; 6     for (i = 0; i < n; i++) { 7         printf("%d ", arr[i]); 8     } 9     printf("\n");10 }11 12 void reverse(int arr[], int n) {13     int i = 0, j = n-1;14     while (i < j) {15         int t = arr[i];16         arr[i] = arr[j];17         arr[j] = t;18         i++, j--;19     }20 }21 22 void rotate(int arr[], int n, int k) {23     reverse(arr, n-k);24     reverse(arr+n-k, k);25     reverse(arr, n);26 }27 28 void merge(int arr[], int s, int n) {29     int i = 0, j = s, k = s;30     while (j < n) {31         while (i < j && arr[i] <= arr[j]) i++;32         if (i == j) break;33         while (j < n && arr[j] < arr[i]) j++;34         rotate(arr+i, j-i, j-k);35         i += j-k, k = j;36     }37 }38 39 void merge_sort(int arr[], int n) {40     if (n > 1) {41         int s = n / 2, t = (n+1) / 2;42         merge_sort(arr, s);43         merge_sort(arr+s, t);44         merge(arr, s, n); // [0, s) & [s, n)45     }46 }47 48 int main() {49     int arr[10] = {1, 4, 2, 8, 5, 7, 9, 3, 0, 6};50     int n = 10;51     merge_sort(arr, n);52     print(arr, n);53     return 0;54 }

 

6. 冒泡排序 (Bubble Sort)

 1 void bubble_sort(int arr[], int n) { 2     int i, j; 3     for (i = 0; i < n; i++) { 4         for (j = n-1; j > i; j--) { 5             if (arr[j] < arr[j-1]) { 6                 int tmp = arr[j]; 7                 arr[j] = arr[j-1]; 8                 arr[j-1] = tmp; 9             }10         }11     }12 }

对其做一点小的优化,使得最好情况下时间复杂度为 O(n),平均情况下执行时间有所减少,但是不会改变平均情况和最坏情况下渐近复杂度的上界 O(n^2)。

 1 void bubble_sort(int arr[], int n) { 2     int i, j; 3     for (i = 0; i < n; i++) { 4         int flag = 0; 5         for (j = n-1; j > i; j--) { 6             if (arr[j] < arr[j-1]) { 7                 int tmp = arr[j]; 8                 arr[j] = arr[j-1]; 9                 arr[j-1] = tmp;10                 flag = 1;11             }12         }13         if (flag == 0) break;14     }15 }

 

7. 选择排序 (Selection Sort)

 1 void what_sort(int arr[], int n) { 2     int i, j; 3     for (i = 0; i < n; i++) { 4         for (j = i+1; j < n; j++) { 5             if (arr[i] > arr[j]) { 6                 int tmp = arr[i]; 7                 arr[i] = arr[j]; 8                 arr[j] = tmp; 9             }10         }11     }12 }13 14 void selection_sort(int arr[], int n) {15     int i, j;16     for (i = 0; i < n; i++) {17         int mini = i, tmp = arr[i];18         for(j = i+1; j < n; j++) {19             if (arr[j] < arr[mini]) {20                 mini = j;21             }22         }23         arr[i] = arr[mini];24         arr[mini] = tmp;25     }26 }

what_sort 其实也是一种选择排序,但是它在性能上比 selection_sort 差一些。

 

8. 逆序对 (Inversions)

在一个序列 A[0 .. n-1] 中,对任意 i, j, 若 i < j 且 A[i] > A[j], 那么 A[i] 和 A[j] 构成一个逆序对。
求序列的逆序对数的最简单的方法就是枚举,时间复杂度为 O(n^2)。

 1 int count_inversions(int arr[], int n) { 2     int inversions = 0; 3     int i, j; 4     for (i = 0; i < n; i++) { 5         for (j = i+1; j < n; j++) { 6             if (arr[i] > arr[j]) inversions++; 7         } 8     } 9     return inversions;10 }

在合并排序的基础上做一些修改,我们能得到一个时间复杂度为O(nlgn)的算法:

 1 void reverse(int arr[], int n) { 2     int i = 0, j = n-1; 3     while (i < j) { 4         int t = arr[i]; 5         arr[i] = arr[j]; 6         arr[j] = t; 7         i++, j--; 8     } 9 }10 11 void rotate(int arr[], int n, int k) {12     reverse(arr, n-k);13     reverse(arr+n-k, k);14     reverse(arr, n);15 }16 17 int merge(int arr[], int s, int n) {18     int inversions = 0;19     int i = 0, j = s, k = s;20     while (j < n) {21         while (i < j && arr[i] <= arr[j]) i++;22         if (i == j) break;23         while (j < n && arr[j] < arr[i]) j++;24         rotate(arr+i, j-i, j-k);25         inversions += (j-k) * (k-i);26         i += j-k, k = j;27     }28     return inversions;29 }30 31 int count_inversions(int arr[], int n) {32     int inversions = 0;33     if (n > 1) {34         int s = n / 2, t = (n + 1) / 2;35         inversions += count_inversions(arr, s);36         inversions += count_inversions(arr+s, t);37         inversions += merge(arr, s, n);38     }39     return inversions;40 }

* 最后,顺便提一下,本文所介绍的三种排序算法都是稳定的。