归并排序(这里主要介绍二路归并)的基本思想是:将排序文件看成为 n 个长度为 1 的有序子文件,把这次子文件两两归并(二路归并),使得到 [n/2] 个长度为 2 的 有序子文件; 然后再把这[n/2] 个有序文件的子文件两两归并,如此反复,直到最后得到一个长度为 n的有序文件为止, 这种排序方法称为二路归并排序。
通过一个案例来说明其具体流程, 假设序列 为 : 125 11 22 34 15 44 76 66 100 8 14 20 2 5 1 共 15 个元素
初始状态: [125] [11] [22] [34] [15] [44] [76] [66] [100] [8] [14] [20] [2] [5] [1] 初始每个元素看作是一个组,共 15 组
一轮归并: [11 125] [22 34] [15 44] [66 76] [8 100] [14 20] [2 5] [1] 两两归并,落单的单独作为一组, 共 8 组
二轮归并: [11 22 34 125] [15 44 66 76] [8 14 20 100] [1 2 5] 两两归并,共 4 组
三轮归并: [11 15 22 34 44 66 76 125] [1 2 5 8 14 20 100] 两两归并,共 2 组
四轮归并: [1 2 5 8 11 14 15 20 22 34 44 66 76 100 125] 两两归并,共 1 组,归并完成
二路归并排序中的核心操作时将数组中前后相邻的两个有序序列归并为一个有序序列。在实现过程中需要引入另外一个数组用于存放归并好的结果。
参考代码:
1 #include <stdio.h> 2 3 #define MAX_NUM 80 4 5 6 void Merge(int* a, int * b, int low, int m,int high) 7 { 8 int i = low; 9 int j = m+1; 10 int k = low; 11 12 while(i <= m && j <= high) 13 { 14 if(a[i] < a[j]) 15 b[k++]= a[i++]; 16 else 17 b[k++] = a[j++]; 18 } 19 20 while(i <= m) 21 b[k++] = a[i++]; 22 23 while(j <= high) 24 b[k++] = a[j++]; 25 26 for( i = low; i <= high;i++) 27 { 28 printf("%d ",b[i]); 29 } 30 printf(" "); 31 32 33 } 34 void MergePass(int* a, int* b, int len, int n) 35 { 36 int i; 37 for(i = 0; i+2*len-1 < n; i = i+ 2*len) 38 Merge(a,b,i,i+len-1,i+2*len-1); 39 40 if(i+len -1 < n) // 如果还有两个子文件,其中最后一个长度小于len 41 Merge(a,b,i,i+len-1,n-1); 42 else 43 { 44 for(int j = i; j <n; j++) 45 b[j] = a[j]; 46 } 47 48 printf("\n"); 49 } 50 51 52 void mergesort(int* a, int* b,int n) 53 { 54 int len = 1; 55 56 while(len < n) 57 { 58 MergePass(a,b,len,n); 59 len = len * 2; 60 MergePass(b,a,len,n); 61 len = len * 2; 62 } 63 } 64 65 int main(int argc, char* argv[]) 66 { 67 int a[MAX_NUM]; 68 int b[MAX_NUM]; 69 int n; 70 71 printf("Input total numbers: "); 72 scanf("%d",&n); 73 74 if( n > MAX_NUM ) n = MAX_NUM; 75 76 for(int i = 0; i < n;i++) 77 { 78 scanf("%d",&a[i]); 79 b[i] = a[i]; 80 } 81 82 printf("归并步骤:\n"); 83 mergesort(a,b,n); 84 85 printf("归并排序后的结果:\n"); 86 for(int i = 0; i < n;i++) 87 { 88 printf("%d ",a[i]); 89 } 90 printf("\n\n"); 91 92 return 0; 93 }
案例运行结果:
归并排序算法的效率与稳定性分析
二路归并排序的过程需要进行[log n] 趟, 每一轮归并排序的操作就是讲两个有序子文件惊醒归并,而每一对有序子文件归并时,记录的比较次数均小于或等于记录的移动次数,记录移动次数均等于文件中记录的个数,每一轮归并的时间复杂度为O(n)。因此,二路归并排序的时间复杂度为O(n log n)。
二路归并排序时稳定的,因为在每两个有序子文件归并是,若分别在两个有序子文件中出现有相同关键字的记录Merge 算法能够使前一个子文件中同一个关键字的记录先复制,后一个文件中的同一关键字的记录被后复制,从而确保了它们的相对次序不会改变。
注:主要参考彭军、向毅主编的 《数据结构与算法》