[转]归并排序:二路归并

归并排序(Merge Sort)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列。

归并排序的具体做法:

 

  1. 把原序列不断地递等分,直至每等份只有一个元素,此时每等份都是有序的。
  2. 相邻等份合,不断合并,直至合并完全。

二路归并

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序最常用的是二路归并,即把两个小的有序的序列和并成一个大的有序序列:合二为一。

一个二路归并的流程图是这样的:

多路归并无非是多个有序的小序列合并成一个大的有序序列,道理和二路归并一样。

先来看下如何把两个有序的序列合并成一个大的有序序列,代码如下:

 

 
  1.  
    /*
  2.  
    *把有序序列a和b,合并成c
  3.  
    *该算法成立前提: a和b已经有序
  4.  
    */
  5.  
    void merge(int a[], int na, int b[], int nb, int c[])
  6.  
    {
  7.  
    if(a && b && c && na >0 && nb >0)
  8.  
    {
  9.  
    int i,j,k;
  10.  
    i = j = k = 0;
  11.  
    //不断地比较a和b的头部元素,较小的存入c
  12.  
    while(i < na && j < nb)
  13.  
    {
  14.  
    if(a[i] <= b[j]) // <= 保持算法的稳定性
  15.  
    c[k++] = a[i++];
  16.  
    else
  17.  
    c[k++] = b[j++];
  18.  
    /*另一种更有效的做法是这样的
  19.  
    while(i < na && a[i] <= b[j])
  20.  
    c[k++] = a[i++];
  21.  
    while(j < nb && b[j] < a[i])
  22.  
    c[k++] = b[j++];
  23.  
    */
  24.  
    }
  25.  
    //把a或b中剩余的元素直接存入c
  26.  
    /* 也可以这样:
  27.  
    * memcpy(c+k, a+i, (na-i)sizeof(int));
  28.  
    * 下同
  29.  
    */
  30.  
    while(i < na)
  31.  
    c[k++] = a[i++];
  32.  
    while(j < nb)
  33.  
    c[k++] = b[j++];
  34.  
    }
  35.  
    }
 

 

可以看出,二路归并的时间复杂度是O(n),n是原序列的数据规模。以上代码是归并排序的基础,弄懂了它,就很好写归并排序了,看下归并排序的流程图:

可以看出,上半部分不断地递归深入:不断地均分原序列,直到每一部分只含有一个元素。下半部分,开始递归返回,通过反复调用二路归并算法,把相邻的有序子序列合并成一个规模更大的序列。

理解了这些,相信就很容易写出归并排序的代码了:

 

 
  1.  
    //把[first, mid]和[mid+1, last]范围内的数据合并
  2.  
    void mergeArray(int a[], int b[], int first, int mid, int last)
  3.  
    {
  4.  
    int i, j, k;
  5.  
    i = first, j = mid + 1, k = 0;
  6.  
    while (i <= mid && j <= last)
  7.  
    {
  8.  
    while(i <= mid && a[i] <= a[j])
  9.  
    b[k++] = a[i++];
  10.  
    while(j <= last && a[j] < a[i])
  11.  
    b[k++] = a[j++];
  12.  
    }
  13.  
    /* 也可以这样:
  14.  
    * memcpy(b+k, a+i, (mid-i+1)sizeof(int));
  15.  
    * 下同
  16.  
    */
  17.  
    while (i <= mid)
  18.  
    b[k++] = a[i++];
  19.  
    while (j <= last)
  20.  
    b[k++] = a[j++];
  21.  
    //[first,last]范围内的数据已有序,则写回原数组
  22.  
    for (i = 0; i < k; i++)
  23.  
    a[first + i] = b[i];
  24.  
    }
  25.  
    void mergesort(int a[], int b[], int first, int last)
  26.  
    {
  27.  
    if (first < last)
  28.  
    {
  29.  
    int mid = first + ((last - first) >> 1);
  30.  
    mergesort(a, b, first, mid);
  31.  
    mergesort(a, b, mid + 1, last);
  32.  
    mergeArray(a, b, first, mid, last);
  33.  
    }
  34.  
    }
  35.  
    void MergeSort(int a[], int n)
  36.  
    {
  37.  
    if (a && n > 1)
  38.  
    {
  39.  
    int *b = new int[n]; //构建辅助数组
  40.  
    mergesort(a, b, 0, n - 1);
  41.  
    delete[]b;
  42.  
    }
  43.  
    }
 

 

 

 

在排序过程中,我们使用了一个相同大小的临时辅助数组。

算法分析:

 

1.算法的复杂度

对数组长度为n的序列进行归并排序,则大约要进行logn次归并,每一次合并都是线性时间O(n)。故粗略的计算出归并排序的时间复杂度是O(nlogn)(最好、最差都是这样)。空间复杂度是O(n)。详细的时间复杂度分析是这样的:

对长度为n的序列归并排序,需要递归的对长度为n/2的子序列进行归并排序,最后把两段子序列二路归并。递推关系是这样的:T(n)=2T(n/2)+O(n),显然T(1)=O(1),解得T(n)=o(nlogn)。

2.稳定性

归并排序是稳定的,并且是时间复杂度为o(nlogn)的几种排序(快速排序堆排序)中唯一稳定的排序算法。

3.存储结构

顺序存储和链式存储都行。

另外,归并排序多用于外排序中。

 

转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/34463409

 

若是有所帮助,顶一个哦!

 

 

所有内容的目录

 

 

 

 


---------------------
作者:苏叔叔
来源:CSDN
原文:https://blog.csdn.net/zhangxiangDavaid/article/details/34463409
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值