Fork me on GitHub

数据结构与算法——十个排序算法之五 · 归并排序

算法简绍:

归并排序(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

 

 

 

演示总结:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

 

动图演示(来源 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 }

 

posted @ 2020-12-12 16:38  索智源  阅读(346)  评论(0)    收藏  举报