排序算法——归并排序

  如果觉得前面的几种排序很简单的话,那么对于归并排序算法的实现就稍微难一点点了,我相信很多同学学完数据结构后,并不能很熟练的写出归并算法,

然而,归并排序以及归并排序的变体在互联网在线笔试题是很常见的。接下来就来学习数组的归并排序,为何要强调是数组的归并排序,因为,今天在OJ上刷题

的时候,遇到了单链表的归并排序,因此,有必要将其区分开来,后面会写一篇单链表归并排序算法。

  这里所说的归并指的是二路归并,即将两个有序的子表,归并为一个表,使这个表中的元素仍然有序。假设有两个子表存放在同一个数组相邻的位置上:

A[s, mid]表示第一个子表,A[mid+1, t]表示第二个子表,现在如果把这两个子表合并成,并且使的数组A[s, t]有序?可以借助额外的内存空间R,同时从子表

A[s, mid]和A[mid+1, t]中取元素进行比较,将较小的存在辅助空间R中,最后将辅助空间R中的元素全部复制回数组A中,排序完成

  两个子表的合并算法,代码如下:

#include <stdlib.h>
#include <stdio.h>

void Merge(int A[], int s, int mid, int t)          // 给定数组的区间以及中间元素的位置索引
{  
    int i = s, j = mid + 1, k = 0;
    int *R;
    R = (int *)malloc(sizeof(int)*(t-s+1))          // 动态分配内存
    while(i<=mid && j<=t)
    {
        if(A[i] < A[j])                             // 将第一个子表中的元素存放在R中
        {
            R[k] = A[i];
            k++;
            i++;
        }
        else
        {
            R[k] = A[j];                            // 将第二个子表中的元素存放在R中
            k++;
            j++;
        }
    }
    while(i<=mid)                                   // 将剩余的部分存入R
    {
        A[k] = A[i];
        k++;
        i++
    }
    while(j<=t)                                     // 将剩余的部分存入R
    {
        A[k] = A[j];
        k++;
        j++;
    }
    for(k = 0, i = s; i <= t; i++, k++)             // 将R中的元素拷贝到A中
        A[i] = R[k];
}

  Merge()函数只是实现了一次归并,如果一个序列,被划分为多个长度为length的子序列(最后一个子序列长度可以小于length),那么就需要多次两两合并,

也就需要多次调用Merge()函数,让相邻的两个子序列合并。这里需要注意的是:如果一个序列被分为偶数个子序列,那么相邻两个子序列恰好可以两两合并,

如果一个序列被分为奇数个子序列,则最后一个子序列不参与合并

  多个子序列两两合并的代码如下:

void MultiMerge(int A[], int length, int n)
{
    int i;
    for(i = 0; i+2*length-1 < n; i = i+2*length)    // 将相邻的子表两两合并
        Merge(A, i, i+length-1, i+2*length-1);
    if(i+length-1 < n)                              // 如果i+2*length-1>=n 且 i+length-1<n
        Merge(A, i, i+length-1; n-1);               // 说明序列被分为偶数个子序列,且最后一个子序列长度小于length
}

  现在完成了将多个子序列两两合并,但是这只实现了归并排序的一趟排序。因此,我们可以将数组A看做是n个长度为1的子序列,然后将其子序列两两合并,然后,

再将其看做n/2个长度为2的子序列,然后两两合并,以此类推;最后整个序列A都是有序的。

  二路归并算法实现如下:

void MergeSort(int A[], int n)
{
    int length;
    for(length = 1; length < n; length = 2*length)
        MultiMerge(A, length, n);
}

  归并排序的时间复杂度为O(nlog2n),时间复杂度为0(n),归并排序是稳定的排序。

 

  

posted @ 2017-07-13 15:41  称霸西关一条街  阅读(329)  评论(0编辑  收藏  举报