数据结构与算法——十个排序算法之五 · 归并排序
算法简绍:
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:
- 自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
- 自下而上的迭代;
和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间。
算法演示:
当两个组数据已经有序,我们可以通过如下方式让两组数据快速有序,
| 1 | 3 | 6 | 7 |
| 2 | 4 | 6 | 8 |
我们可以依次从两组中取最前面的那个最小元素依次有序放到新的数组中,然后再把新数组 中有序的数据拷贝到原数组中快速完成排序.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
依靠这种思想,有了如下的排序方法!
具体步骤:
对于下面这一组待排序的数组
| 163 | 161 | 158 | 165 | 171 | 170 | 163 | 159 | 162 |
先以中间为界,把其均分为 A 和 B 两个数组(如果是奇数个,允许两组数相差一个)
A组:
| 163 | 161 | 158 | 165 |
B组:
| 171 | 170 | 163 | 159 | 162 |
如果 A 和 B 两组数据能够有序,则我们可以通过上面的方式让数组快速排好序。 此时,A 组有 4 个成员, B 组有 5 个成员, 但两个数组都无序,然后我们可以采用分治法继 续对 A 组和 B 组进行均分,以 A 组为例,又可以均分 A1 和 A2 两个组如下:
A组:
| 163 | 161 |
B组:
| 158 | 165 |
均分后,A1 组和 A2 组仍然无序,继续利用分治法细分,以 A1 组为例,A1 又可分成如下 两组
A11 组:
| 163 |
A12 组:
| 161 |
数组细分到一个元素后,这时候,我们就可以采用归并法借助一个临时数组将数组 A1 有序化! A2 同理!
A组:
| 161 | 163 |
B组:
| 158 | 165 |
依次类推,将 A1 组和 A2 组归并成有序的 A 组, B 组同理!
A组:
| 158 | 161 | 163 | 165 |
B组:
| 159 | 162 | 163 | 170 | 171 |
最后,将 A 和 B 组合并,就得到了完整的有序的结果!
| 158 | 159 | 161 | 162 | 163 | 163 | 165 | 170 | 171 |
演示总结:
-
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
-
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
-
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
-
重复步骤 3 直到某一指针达到序列尾;
-
将另一序列剩下的所有元素直接复制到合并序列尾。
动图演示(来源 runoob.com):

代码实现:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 void mergeAdd_demo(int arr[], int left, int mid, int right) 6 { 7 int temp[64]={0}; 8 int i = left; //指向左边数组最小的元素位置 9 int j = mid; //指向右边数组最小的元素位置 10 int k = 0; //临时数组的下标 11 while( i<mid && j<=right) 12 { 13 if(arr[i]<arr[j]) 14 { 15 temp[k++] = arr[i++]; 16 } 17 else 18 { 19 temp[k++] = arr[j++]; 20 } 21 } 22 23 while(i< mid) 24 { 25 temp[k++] = arr[i++]; 26 } 27 28 while(j<= right) 29 { 30 temp[k++] = arr[j++]; 31 } 32 33 //把 temp 中的内容拷贝到 arr 数组中 34 memcpy(arr+left, temp, sizeof(int) * (right - left + 1)); 35 36 } 37 38 void mergeAdd(int arr[], int left, int mid, int right, int *temp) 39 { 40 //int temp[64]={0}; 41 int i = left; //指向左边数组最小的元素位置 42 int j = mid; //指向右边数组最小的元素位置 43 int k = left; //临时数组的下标 44 45 while( i<mid && j<=right) 46 { 47 if(arr[i]<arr[j]) 48 { 49 temp[k++] = arr[i++]; 50 } 51 else 52 { 53 temp[k++] = arr[j++]; 54 } 55 } 56 57 while(i< mid) 58 { 59 temp[k++] = arr[i++]; 60 } 61 62 while(j<= right) 63 { 64 temp[k++] = arr[j++]; 65 } 66 67 //把 temp 中的内容拷贝到 arr 数组中 68 69 memcpy(arr+left, temp+left, sizeof(int) * (right - left + 1)); 70 71 } 72 73 void mergeSort(int arr[], int left, int right, int *temp) //归并排序 74 { 75 int mid = 0; 76 if(left < right) 77 { 78 mid = left +(right - left)/2; 79 mergeSort(arr, left, mid, temp); 80 mergeSort(arr, mid + 1, right, temp); 81 mergeAdd(arr, left, mid + 1, right, temp); 82 } 83 } 84 85 int main(void) 86 { 87 int beauties[]={10, 11, 12, 13, 2, 4, 5, 8}; 88 int len = sizeof(beauties)/sizeof(beauties[0]); 89 int *temp = new int[len]; 90 91 //int mid = len/2; 92 mergeSort(beauties, 0, len - 1, temp); 93 94 //mergeAdd(beauties, 0, mid, len-1, temp); 95 printf("执行合并:\n"); 96 97 for(int i=0; i<len; i++) 98 { 99 printf("%d ", beauties[i]); 100 } 101 102 system("pause"); 103 104 return 0; 105 }


浙公网安备 33010602011771号