首页 > 代码库 > 算法之归并排序的递归与非递归的实现

算法之归并排序的递归与非递归的实现

一.什么是归并排序

    归并排序就是将多个有序的数据段合成一个有序的数据段,如果参与合并的只有两个有序的数据段,则称为二路归并。与快速排序和堆排序相比,其最大的特点是一种稳定的算法,算法的平均时间复杂度O(nlog2n)。

二.归并排序的基本思路

    (1).对于一个原始的待排序表,可以将R[1]到R[n]可以看做是n个长度为1的有序表,即分解。

    (2).进行第一趟归并,即将上述的n个子序两两合并,得到 n/2向上取整 个有序表,若n为奇数,则归并到最后一个子序列长度为1,即合并。

    (3).再将两个 n/2向上取整 个有序表两两合并,如此重复下去,直到得到一个长度为n的有序表,这种排序方法也叫2-路归并排序。

     原理如下图

 

                                         技术分享

                                                      以[63,95,84,46,18,24,27,31,46]为例

三. 实现归并排序之前,需要调用"一次归并“和”一趟归并“,那什么是一次归并?什么是一趟归并呢?

     (1).一次归并:把首尾相接的两个有序表R[low...mid]、R[mid+1...high]归并成有序表R[low...high].

      (2) .一趟归并:把一些相互连接的有序表依次两两归并成一些更大的有序表。

 

 1.一次归并代码如下

//一次归并排序 void Merge(int low,int m,int high)         {                // 将两个有序的子文件R[low..m)和R[m+1..high]归并成一个有序的子文件R[low..high]            int i=low;    int j=m+1;    int p=0;            //置初始值          int *R1;            //R1是局部向量        R1=(int *)malloc((high-low+1)*sizeof(int));          if(!R1)                        //申请空间失败           {                puts("空间申请失败");               return;           }           while(i<=m&&j<=high)                  // 两子文件非空时取其小者输出到R1[p]上               R1[p++]=(R[i]<=R[j])?R[i++]:R[j++];           while(i<=m)                          // 若第1个子文件非空,则复制剩余记录到R1中                 R1[p++]=R[i++];           while(j<=high)                      // 若第2个子文件非空,则复制剩余记录到R1中              R1[p++]=R[j++];           for(p=0,i=low;i<=high;p++,i++)                 R[i]=R1[p];                    // 归并完成后将结果复制回R[low..high] }   

2.一趟归并排序代码如下

 1 //一趟归并排序  2 void mergepass(int n,int len)  3 { 4     int i,t; 5     i=1; 6     while(i<=n-2*len+1) 7     { 8         Merge(i,i+len-1,i+2*len-1); 9         i=i+2*len;10     }11     if(i+len-1<n)12        Merge(i,i+len-1,n);13 }

 

   写完”一次归并“和"一趟归并”后,接下来就是编写二路归并了,二路归并就是调用“一次归并”和“一趟归并”,最后组合成一个长度为n的有序表。

二路归并有递归的方法和非递归的方法(即迭代方法),下面就来看一下递归与非递归的实现

(1)非递归的方法

 1 void mergesort(int n)       //非递归的二路归并实现 2 { 3     int len; 4     int *R1;            // R1是局部向量 5     len=1; 6     while(len<n) 7     { 8       mergepass(n,len); 9       len=2*len;10     }11 }

(2)递归方法实现

 1 //递归的二路归并排序  2 void MergeSortDC(int low,int high)  3 {                                     //用分治法对R[low..high]进行二路归并排序         4    int mid;         5    if(low<high)          6    {                                 // 区间长度大于1           7      mid=(low+high)/2;                //分解 */     8      MergeSortDC(low,mid);           // 递归地对R[low..mid]排序     9      MergeSortDC(mid+1,high);           //递归地对R[mid+1..high]排序           10      Merge(low,mid,high);               // 组合,将两个有序区归并为一个有序区          11    }  12 }   

     讲到这里,归并排序的思想方法就讲完了,然后就可以自己写main()函数了,然后调用上面的自定义函数就可以实现了

下面是完整的代码实现,仅供参考,如有问题,欢迎指教

  1 #include <stdio.h>    2 #include<malloc.h>  3 #include<stdlib.h>  4 #define MAX 100   5 int R[MAX];     6 //一次归并排序   7 void Merge(int low,int m,int high)           8 {                // 将两个有序的子文件R[low..m)和R[m+1..high]归并成一个有序的子文件R[low..high]          9     int i=low; 10     int j=m+1; 11     int p=0;            //置初始值       12     int *R1;            //R1是局部向量     13     R1=(int *)malloc((high-low+1)*sizeof(int));       14     if(!R1)                        //申请空间失败        15     {          16        puts("空间申请失败");         17        return;        18     }        19     while(i<=m&&j<=high)                  // 两子文件非空时取其小者输出到R1[p]上        20         R1[p++]=(R[i]<=R[j])?R[i++]:R[j++];        21     while(i<=m)                          // 若第1个子文件非空,则复制剩余记录到R1中          22         R1[p++]=R[i++];        23     while(j<=high)                      // 若第2个子文件非空,则复制剩余记录到R1中       24         R1[p++]=R[j++];        25     for(p=0,i=low;i<=high;p++,i++)          26         R[i]=R1[p];                    // 归并完成后将结果复制回R[low..high]  27 }    28 //递归的二路归并排序  29 void MergeSortDC(int low,int high)  30 {                                     //用分治法对R[low..high]进行二路归并排序         31    int mid;         32    if(low<high)          33    {                                 // 区间长度大于1           34      mid=(low+high)/2;                //分解 */     35      MergeSortDC(low,mid);           // 递归地对R[low..mid]排序     36      MergeSortDC(mid+1,high);           //递归地对R[mid+1..high]排序            37      Merge(low,mid,high);               // 组合,将两个有序区归并为一个有序区           38    }   39 }    40 //一趟归并排序  41 void mergepass(int n,int len)  42 { 43     int i,t; 44     i=1; 45     while(i<=n-2*len+1) 46     { 47         Merge(i,i+len-1,i+2*len-1); 48         i=i+2*len; 49     } 50     if(i+len-1<n) 51        Merge(i,i+len-1,n); 52 } 53 void mergesort(int n)  54 { 55     int len; 56     int *R1;            // R1是局部向量 57     len=1; 58     while(len<n) 59     { 60       mergepass(n,len); 61       len=2*len; 62     } 63 } 64 void main()  65 {   66     int i,n,d;      67     puts("请输入数组的长度:"); 68     scanf("%d",&n);    69     if(n<=0||n>MAX)   70     {    71         printf("n 必须大于 0 小于 %d.\n",MAX);    72         exit(0);   73     }    74     puts("请依次输入数组的数据:");   75     for(i=1;i<=n;i++)    76       scanf("%d",&R[i]);    77     puts("排序前的数组数据为:");   78     for(i=1;i<=n;i++)    79        printf("%4d",R[i]);   80    printf("\n\n      递归与非递归的合并排序       \n"); 81    printf("\n      1.递归合并排序               \n"); 82    printf("\n      2.非递归合并排序             \n"); 83    printf("\n请输入您的选择:"); 84    scanf("%d",&d); 85    if(d==1) 86    { 87     MergeSortDC(1,n);    88     puts("\n递归归并排序后:");   89     for(i=1;i<=n;i++)    90        printf("%4d",R[i]);   91    }  92    else if(d==2)  93    { 94        mergesort(n); 95        puts("\n非递归归并排序后:");   96     for(i=1;i<=n;i++)    97        printf("%4d",R[i]);  98    } 99    else100      printf("您输入的菜单号有误!"); 101 }

 

      

 

 

 

    

 

算法之归并排序的递归与非递归的实现