码迷,mamicode.com
首页 > 编程语言 > 详细

归并排序

时间:2015-03-07 06:13:44      阅读:259      评论:0      收藏:0      [点我收藏+]

标签:

二路归并排序算法

  一、基本思想:将两个有序表放在同一数组中相邻的位置上,如 R[low...mid]  和 R[mid+1...high],每次从两个段中取一个较小的数据顺序的放入数组 R´中,即将两个有序的子表合并成一个有序的表。

  

  技术分享  

 

  二、C 语言代码:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 /**
  5  * 首先介绍将两个有序表直接归并为一个有序表的算法 Merge(), 假设两个有序表放在同一个数组中相邻的位置上: R[low...mid], R[mid+1...high] 
  6  先将它们合并到一个局部的暂存数组 R1 中, 待合并完以后将 R1 复制回 R 中. 为了简便, 称 R[low...mid] 为第一段, R[mid+1...high] 为第二段
  7  每次从两个段中取出一个记录进行关键字的比较,将较小者放入 R1 中, 最后将各段中余下的部分直接复制到 R1 中. 这样 R1 是一个有序的表, 再将
  8  其复制回 R 中. 算法如下: 
  9 **/
 10 
 11 void merge(RecType R[], int low, int mid, int high)
 12 {
 13     //k 是 R1 的下标, i、j 分别为第 1、2 段的下标. 
 14     RecType *R1;
 15     int i = low;
 16     int j = mid + 1;
 17     int k = 0;
 18     
 19     //动态分配空间 
 20     R1 = (RecType *) malloc((high - low + 1) * sizeof(RecType));
 21     
 22     //在第一段和第二段均未扫描完成时循环 
 23     while (i <= mid && j <= high) {
 24         if (R[i].key <= R[j].key) { //将第一段记录放入 R1 中 
 25             R1[k] = R[i];
 26             i++;
 27             k++; 
 28         } else { //将第二段记录放入 R2 中 
 29             R1[k] = R[j];
 30             j++;
 31             k++;
 32         }
 33     } 
 34     
 35     //将第一段剩余的部分复制到 R1  
 36     while (i < mid) {
 37         R1[k] = R[i];
 38         i++;
 39         k++;
 40     } 
 41     
 42     //将第二段剩余的部分复制到 R2
 43     while (j < high) {
 44         R1[k] = R[j];
 45         j++;
 46         k++;
 47     } 
 48     
 49     //将 R1 复制回 R 中 
 50     for (k = 0; i = low; i <= high; K++; i++) {
 51         R[i] = R1[k]; 
 52     }
 53 } 
 54 
 55 /**
 56  * Merge() 算法实现了一次归并, 其中使用的辅助空间正好是要归并的元素个数, 接下来需要利用 Merge() 解决一趟归并问题, 在某趟归并中,
 57  设各个子表长度为 length(最后一个子表的长度可能小于 length), 则归并前 R[0..n-1] 中共 [n/length] 个有序的子表: R[0..length-1], 
 58  R[length..2length-1], ..., R[([n/length]) * length..n-1]. 调用 Merge() 算法将相邻的一对子表进行归并时, 必须对表的个数可能是奇数
 59  和最后一个子表的长度小于 length 这两种特殊情况作特殊处理: 若子表个数为奇数, 则最后一个子表无须和其他子表归并(即轮空); 若子表个数
 60  为偶数, 则要注意到最后一对子表的区间上届是 n-1, 算法如下: 
 61 **/
 62 
 63 //对整个数列进行一趟归并 
 64 void mergePass(RecType R[], int length, int n)
 65 {
 66     int i;
 67     
 68     //归并 length 长的两相邻子表 
 69     for (i = 0; i + 2*length -1 < n; i = i + 2*length) {
 70         merge(R, i, i + length-1, i + 2*length-1); 
 71     } 
 72     
 73     //余下的两个子表, 后者的长度小于 length, 归并这两个子表 
 74     if ( i + length-1 < n ) {
 75         merge(R, i, i + length-1, n-1);
 76     }
 77 }
 78 
 79 
 80 //归并排序实现方法一: 自底向上
 81 /**
 82  * 自底向上的基本思想: 第一趟归并排序时, 将待排序的表 R[0..n-1] 看作是 n 个长度为 1 的有序子表, 将这些子表两两归并, 若 n 为偶数,
 83  则得到了 [n/2] 个长度为 2 的有序子表; 若 n 为奇数, 则最后一个子表轮空(不参与归并), 故本次归并完成后, 前 [n/2]-1 个有序子表长度
 84  为 2, 但最后一个子表的长度仍然为 1; 第二趟归并则是将第一趟归并所得到的 [n/2] 个有序表两两归并, 如此反复, 直到合成最后一个长度为 
 85  n 的有序表位置. 上述的每次归并操作, 均是将两个有序的子表合并成一个有序的子表, 故称其为"二路归并排序". 算法如下: 
 86 **/
 87 
 88 void mergeSort(RecType R[], int n)
 89 {
 90     int length;
 91     for (lenght = 1; length < n; length = 2*length) {
 92         mergePass(R, length, n);
 93     } 
 94 }
 95 
 96 //归并排序实现方法二: 自顶向下
 97 /**
 98  * 上述的自底向上归并算法效率较高, 但可读性较差, 若采用自顶向下的方法设计, 算法会更加简洁, 设归并排序的当前区间为 R[low..high], 则
 99  递归归并的两个步骤:
100      1.分解, 将当前区间 R[low..high] 一分为二, 即求 mod = (low + high)/2; 递归地对两个子区间 R[low..mid] 和 R[mid+1..high] 进行继续
101     分解, 其终结条件是子区间长度为 1(因为一个记录的子表一定是有序表).
102     2.归并, 与分解过程相反, 将已排序的两个子区间 R[low..mid] 和 R[mid+1..high] 归并为一个有序的区间 R[low..high]. 算法如下:
103 **/ 
104 
105 void mergeSortDC(RecType R[], int low, int high)
106 {
107     int mid;
108     
109     if (low < high) {
110         mid = (low + high) / 2;
111         mergeSortDC(R, low, mid);
112         mergeSortDC(R, mid+1, high);
113         merge(R, low, mid, high);
114     }
115 } 
116 
117 void mergeSort(RecType R[], int n)
118 {
119     mergeSortDC(R, 0, n-1);
120 }

 

  三、算法分析

    时间复杂度:由算法代码可知,归并排序的算法较为复杂,中间涉及多个过程和算法,可以根据其基本思想来粗略估算时间复杂度,对于 n 个记录而言,二路选择需要移动 log2n+1 次,然后,被选出来的记录再经过 n-1 次比较完成排序,故对于二路归并算法而言:

      关键字比较和记录移动的最少次数是 (n-1)log2n+1,算法的时间复杂度为 O(nlog2n)。

      关键字比较和记录移动的最多次数为 (n-1)log2n+1,算法的时间复杂度为 O(nlog2n)。

      关键字平均比较和记录移动次数为 (n-1)log2n+1,算法的时间复杂度为 O(nlog2n)。

      

    空间复杂度:由算法代码可知,所需的额外空间有 n-1 个 tmp 变量,故二路归并排序算法空间复杂度为 O(n)。

 

  四、思考

    归并排序是一种非常有效、非常典型的快速算法,那么,归并排序是如何体现分治思想的呢?

 

 

 

 

 

  

   

归并排序

标签:

原文地址:http://www.cnblogs.com/lishiyun19/p/4319633.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!