排序算法:归并排序

Posted 不要做程序员的小松鼠

tags:

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

归并排序

1. 将若干有序序列逐步归并为一个有序序列。
2. 二路归并:最简单,将若干有序序列两两归并,直至形成一个有序序列。
3. 采用的分治法。
4. 二路归并非递归思路:
    1. 将序列分成n个序列,每个序列一个元素,这样可以任务每个序列都是有序序列。
    2. 逐一合并两个相邻的序列,使得每个序列长度为2.
    3. 重复步骤2,直到序列的长度为n。
    4. 子序列合并过程中有三种情况
        1. 子序列数目为偶数且子序列长度相等。
        2. 子序列数目为偶数且最后一个子序列长度小于其他子序列。
        3. 子序列数目为奇数(最后剩下一个子序列)。
5.比较占内存、效率高、稳定的排序算法。

复杂度

1. 一趟归并排序:O(n)。
2. 整个归并排序,需要【log2n】取整趟,例如:8个数三趟。
3. 所以总的视觉代价是O(nlogn),最好、最坏、平均的时间性能。
4. 空间复杂度:非递归时候只需要O(n),小于递归时候占的内存,实际使用中推荐使用非递归方法。
5. 排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。 

图示

代码实现

非递归方法

void Merge(int r[ ], int r1[ ], int s, int m, int t)
{
/*
    一次归并算法
    s:start m:middle t:tail
        r数组分为s——m,和m+1——t,两个部分。
        r1为归并好的数组。*/

    int i=s;
    int j=m+1;
    int  k=s;
    while (i<=m && j<=t)
    {   
        if (r[i]<=r[j]) 
        {
            r1[k++]=r[i++];   //取r[i]和r[j]中较小者放入r1[k]
        }
        else 
        {
            r1[k++]=r[j++]; 
        }
    }
    if (i<=m) while (i<=m)      //若第一个子序列没处理完,则进行收尾处理
    {
        r1[k]=r[i]; 
        i++;
        k++;
    }   
    else  while (j<=t)       //若第二个子序列没处理完,则进行收尾处理
    {
        r1[k]=r[j];
        k++;
        j++;
    }
}
void MergePass(int r[ ], int r1[ ], int n, int h)
{
    //根据步长将相邻的两个子序列合并
    int i=1;
    //待归并记录至少有两个长度为h的子序列,之所以n+1,是因为我们从r[1]开始

    while(i<=(n-2*h+1))
    {
        //若每个子序列相等且是偶数个,则一直走这个循环1 3 …… n-2*h+1 n+1(不成立跳出循环)
        //将相邻的有序序列合并成一个有序序列
        Merge(r, r1, i, i+h-1, i+2*h-1);
        i = i + 2*h;
    }
    //待归并序列中有一个长度小于h,此时分为两段i——i+h-1,i+h——n,可推得i>(n-2*h+1)
    if (i<n-h+1) 
    {   //当最后一个子序列长度小于其他子序列,且子序列数目为偶数
        //n+1-2h<i<n+1-h
        Merge(r, r1, i, i+h-1, n); 
    }
    else 
    {
        //待归并序列中只剩一个子序列,子序列数目为奇数,剩下的孤零零的一个子序列(长度<=h)
        for (int k=i; k<=n; k++)    
        {
            r1[k]=r[k];
        }
    }
 }

void MergeSort1(int r[ ], int r1[ ], int n )
{
    cout<<"MergeSort:"<<endl;
    int h=1;//初始序列长度为1,当序列长度达到n时候,意味着排序完成,此时h>=n
    while (h<n)
    {
        int i ;
        MergePass(r, r1, n, h);
        cout<<"步长h:"<<h<<endl;
        for (i = 1;i<10;i++)
        {
            cout<<r1[i]<<" ";
        }
        cout<<endl;

        h=2*h;//每次序列合并之后,第一个单个序列长度为2h


        int *p = r;
        r = r1;
        r1 = p;
    }
}

递归方法

void Merge(int r[ ], int r1[ ], int s, int m, int t)
{
/*
    一次归并算法
    s:start m:middle t:tail
        r数组分为s——m,和m+1——t,两个部分。
        r1为归并好的数组。*/

    int i=s;
    int j=m+1;
    int  k=s;
    while (i<=m && j<=t)
    {   
        if (r[i]<=r[j]) 
        {
            r1[k++]=r[i++];   //取r[i]和r[j]中较小者放入r1[k]
        }
        else 
        {
            r1[k++]=r[j++]; 
        }
    }
    if (i<=m) while (i<=m)      //若第一个子序列没处理完,则进行收尾处理
    {
        r1[k]=r[i]; 
        i++;
        k++;
    }   
    else  while (j<=t)       //若第二个子序列没处理完,则进行收尾处理
    {
        r1[k]=r[j];
        k++;
        j++;
    }
}
void MergeSort2(int r[ ], int r1[ ], int s, int t)
{ 
    int m;
    if (s==t)
    {
        r1[s]=r[s]; 
    }
    else 
    {
        m=(s+t)/2;
        MergeSort2(r, r1, s, m);    //归并排序前半个子序列
        MergeSort2(r, r1, m+1, t);   //归并排序后半个子序列
        Merge(r1, r, s, m, t);      //将两个已排序的子序列归并

    }
    }

注意:代码中的排序都是从r[1]开始的。

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

高级排序算法之归并排序,快速排序

归并排序(递归非递归以及自然归并排序)算法总结

排序算法之归并排序(Java)

排序算法之归并排序(Java)

看动画学算法之:排序-归并排序

排序算法6 —— 归并排序