1. 程式人生 > >二路歸並排序算法

二路歸並排序算法

class 時間 排序算法 board ces 得到 lib 空間 -i

將兩個按值有序序列合並成一個按值有序序列,則稱之為二路歸並排序,下面有自底向上和自頂向下的兩種排序算法,自頂向下的排序在本文末講述,使用遞歸實現,代碼較簡潔,經供參考。

1. 歸並子算法:把位置相鄰的兩個按值有序序列合並成一個按值有序序列。例如把序列 X[s..u] = {3, 12, 23, 32}和 序列 X[u+1..v] = {2, 5, 8, 99} 合並成序列

Z[s..v] = {2, 3, 5, 8, 12, 23, 32, 99}, 註意合並前的元素都位於同一個有序序列的相鄰位置,合並後的有序序列的下標與合並前的序列總的起始下標相同。

算法過程中需要三個整型變量,i 用來遍歷歸並的前一個有序序列,其初始位置是s;j 用來遍歷歸並的後一個有序序列,其初始值是u+1;q 用來指出歸並後得到的有序序列的末尾元素的位置,其初始值是s。當遍歷完成其中的一個有序序列之後,只需把另一個未結束有序序列的剩余元素復制到合並後的有序序列的末尾。

看代碼:

[cpp] view plain copy
  1. //將有序的X[s..u]和X[u+1..v]歸並為有序的Z[s..v]
  2. void merge(int X[], int Z[], int s, int u, int v)
  3. {
  4. int i, j, q;
  5. i = s;
  6. j = u + 1;
  7. q = s;
  8. while( i <= u && j<= v )
  9. {
  10. if( X[i] <= X[j] )
  11. Z[q++] = X[i++];
  12. else
  13. Z[q++] = X[j++];
  14. }
  15. while( i <= u ) //將X中剩余元素X[i..u]復制到Z
  16. Z[q++] = X[i++];
  17. while( j <= v ) //將X中剩余元素X[j..v]復制到Z
  18. Z[q++] = X[j++];
  19. }


2. 一趟歸並掃描子算法:將參加排序的序列分成若幹個長度為 t 的,且各自按值有序的子序列,然後多次調用歸並子算法merge將所有兩兩相鄰成對的子序列合並成若幹個長度為

2t 的,且各自按值有序的子序列。

若某一趟歸並掃描到最後,剩下的元素個數不足兩個子序列的長度時:

  • 若剩下的元素個數大於一個子序列的長度 t 時,則再調用一次歸並子算法 merge 將剩下的兩個不等長的子序列合並成一個有序子序列
  • 若剩下的元素個數小於或者等於一個子序列的長度 t 時,只須將剩下的元素依次復制到前一個子序列後面。
看代碼: [cpp] view plain copy
  1. /* X[0..n-1]表示參加排序的初始序列
  2. * t為某一趟歸並時子序列的長度
  3. * 整型變量i指出當前歸並的兩個子序列中第1個子序列的第1個元素的位置
  4. * Y[0..n-1]表示這一趟歸並後的結果
  5. */
  6. void mergePass(int X[], int Y[], int n, int t)
  7. {
  8. int i = 0, j;
  9. while( n - i >= 2 * t ) //將相鄰的兩個長度為t的各自有序的子序列合並成一個長度為2t的子序列
  10. {
  11. merge(X, Y, i, i + t - 1, i + 2 * t - 1);
  12. i = i + 2 * t;
  13. }
  14. if( n - i > t ) //若最後剩下的元素個數大於一個子序列的長度t時
  15. merge(X, Y, i, i + t - 1, n - 1);
  16. else //n-i <= t時,相當於只是把X[i..n-1]序列中的數據賦值給Y[i..n-1]
  17. for( j = i ; j < n ; ++j )
  18. Y[j] = X[j];
  19. }

3. 二路歸並排序算法:將參加排序的初始序列分成長度為1的子序列使用mergePass函數進行第一趟排序,得到 n / 2 個長度為 2 的各自有序的子序列(若n為奇數,還會存在一個最後元素的子序列),再一次調用mergePass函數進行第二趟排序,得到 n / 4 個長度為 4 的各自有序的子序列, 第 i 趟排序就是兩兩歸並長度為 2^(i-1) 的子序列得到 n / (2^i) 長度為 2^i 的子序列,直到最後只剩一個長度為n的子序列。由此看出,一共需要 log2n 趟排序,每一趟排序的時間復雜度是 O(n), 由此可知
該算法的總的時間復雜度是是 O(n log2n),但是該算法需要 O(n) 的輔助空間,空間復雜度很大,是 O(n).
看代碼: [cpp] view plain copy
  1. void mergeSort(int X[], int n)
  2. {
  3. int t = 1;
  4. int *Y = (int *)malloc(sizeof(int) * n);
  5. while( t < n )
  6. {
  7. mergePass(X, Y, n, t);
  8. t *= 2;
  9. mergePass(Y, X, n, t);
  10. t *= 2;
  11. }
  12. free(Y);
  13. }

程序總的代碼匯總: [cpp] view plain copy
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. //將有序的X[s..u]和X[u+1..v]歸並為有序的Z[s..v]
  4. void merge(int X[], int Z[], int s, int u, int v)
  5. {
  6. int i, j, q;
  7. i = s;
  8. j = u + 1;
  9. q = s;
  10. while( i <= u && j<= v )
  11. {
  12. if( X[i] <= X[j] )
  13. Z[q++] = X[i++];
  14. else
  15. Z[q++] = X[j++];
  16. }
  17. while( i <= u ) //將X中剩余元素X[i..u]復制到Z
  18. Z[q++] = X[i++];
  19. while( j <= v ) //將X中剩余元素X[j..v]復制到Z
  20. Z[q++] = X[j++];
  21. }
  22. /* X[0..n-1]表示參加排序的初始序列
  23. * t為某一趟歸並時子序列的長度
  24. * 整型變量i指出當前歸並的兩個子序列中第1個子序列的第1個元素的位置
  25. * Y[0..n-1]表示這一趟歸並後的結果
  26. */
  27. void mergePass(int X[], int Y[], int n, int t)
  28. {
  29. int i = 0, j;
  30. while( n - i >= 2 * t ) //將相鄰的兩個長度為t的各自有序的子序列合並成一個長度為2t的子序列
  31. {
  32. merge(X, Y, i, i + t - 1, i + 2 * t - 1);
  33. i = i + 2 * t;
  34. }
  35. if( n - i > t ) //若最後剩下的元素個數大於一個子序列的長度t時
  36. merge(X, Y, i, i + t - 1, n - 1);
  37. else //n-i <= t時,相當於只是把X[i..n-1]序列中的數據賦值給Y[i..n-1]
  38. for( j = i ; j < n ; ++j )
  39. Y[j] = X[j];
  40. }
  41. void mergeSort(int X[], int n)
  42. {
  43. int t = 1;
  44. int *Y = (int *)malloc(sizeof(int) * n);
  45. while( t < n )
  46. {
  47. mergePass(X, Y, n, t);
  48. t *= 2;
  49. mergePass(Y, X, n, t);
  50. t *= 2;
  51. }
  52. free(Y);
  53. }
  54. void print_array(int array[], int n)
  55. {
  56. int i;
  57. for( i = 0 ; i < n ; ++i )
  58. printf("%d ", array[i]);
  59. printf("\n");
  60. }
  61. int main()
  62. {
  63. int array[] = {65, 2, 6, 1, 90, 78, 105, 67, 35, 23, 3, 88, -22};
  64. int size = sizeof(array) / sizeof(int);
  65. mergeSort(array, size);
  66. print_array(array, size);
  67. return 0;
  68. }


歸並排序自頂向下排序,僅供參考: [cpp] view plain copy
  1. void merge2(int *data, int start, int mid, int end)
  2. {
  3. int *temp = (int *)malloc((end-start+1)*sizeof(int));
  4. int i = start;
  5. int j = mid + 1;
  6. int k = 0;
  7. while(i <= mid && j <= end)
  8. {
  9. temp[k++] = data[i] <= data[j] ? data[i++] : data[j++];
  10. }
  11. while(i <= mid)
  12. temp[k++] = data[i++];
  13. while(j <= end)
  14. temp[k++] = data[j++];
  15. k = 0;
  16. for(i = start; i <= end; ++i)
  17. data[i] = temp[k++];
  18. free(temp);
  19. }
  20. //從頂往下
  21. void mergeSort2(int *data, int start, int end, int *des)
  22. {
  23. if(start == end)
  24. return;
  25. int mid = (end + start)/2;
  26. mergeSort2(data, start, mid, des);
  27. mergeSort2(data, mid+1, end, des);
  28. merge2(data, start, mid, end);
  29. }
    1. 轉自:http://blog.csdn.net/caryaliu/article/details/7475700

二路歸並排序算法