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

Posted 笑看风云动

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法之归并排序的递归与非递归的实现相关的知识,希望对你有一定的参考价值。

一.什么是归并排序

    归并排序就是将多个有序的数据段合成一个有序的数据段,如果参与合并的只有两个有序的数据段,则称为二路归并。与快速排序和堆排序相比,其最大的特点是一种稳定的算法,算法的平均时间复杂度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    else
100      printf("您输入的菜单号有误!"); 
101 }

 

      

 

 

 

    

 

以上是关于算法之归并排序的递归与非递归的实现的主要内容,如果未能解决你的问题,请参考以下文章

排序算法:直接插入排序归并排序(递归与非递归实现)

排序算法:直接插入排序归并排序(递归与非递归实现)

排序算法:直接插入排序归并排序(递归与非递归实现)

归并排序的递归算法与非递归

归并排序

(C++)归并排序的递归与非递归实现