zoukankan      html  css  js  c++  java
  • 算法之归并排序的递归与非递归的实现

    一.什么是归并排序

        归并排序就是将多个有序的数据段合成一个有序的数据段,如果参与合并的只有两个有序的数据段,则称为二路归并。与快速排序和堆排序相比,其最大的特点是一种稳定的算法,算法的平均时间复杂度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.
    ",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("
    
          递归与非递归的合并排序       
    ");
     81    printf("
          1.递归合并排序               
    ");
     82    printf("
          2.非递归合并排序             
    ");
     83    printf("
    请输入您的选择:");
     84    scanf("%d",&d);
     85    if(d==1)
     86    {
     87     MergeSortDC(1,n);   
     88     puts("
    递归归并排序后:");  
     89     for(i=1;i<=n;i++)   
     90        printf("%4d",R[i]);  
     91    } 
     92    else if(d==2) 
     93    {
     94        mergesort(n);
     95        puts("
    非递归归并排序后:");  
     96     for(i=1;i<=n;i++)   
     97        printf("%4d",R[i]); 
     98    }
     99    else
    100      printf("您输入的菜单号有误!"); 
    101 }

          

        

  • 相关阅读:
    C++学习(c++17)——2.X.使用string和string_view
    C++学习(c++17)——1.3一个小程序(part2.Datebase类)
    论++i + ++i + ++i
    C++学习(c++17)——1.3 一个小程序(part1.Employee类)
    C++学习(c++17)——1.2 稍微深入研究C++
    VS2019社区版关于Qt的设置
    C++学习(c++17)——1.1 C++基础知识
    2020杭电多校第五场
    2020杭电多校第四场
    2020杭电多校第三场
  • 原文地址:https://www.cnblogs.com/crx234/p/5858488.html
Copyright © 2011-2022 走看看