用归并排序法对一组数据由小到大进行排序,数据分别为 695、458、362、789、12、 15、163、23、2、986。

算法表述:

归并排序的基本原理是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。其实通俗来说,对于一个数来说自身是有序的,然后每次选取两个数使之自身有序,然后每次选取四个数使之自身有序,以后以二倍增长选择,进行排序。将若将两有个有序表合成一个有序表,成为二路归并。(具体可以依据下图进行分析)

算法执行过程分析:

例如:15、2、35、6、23、11、5
说明:其中s1,s2,e1,e2 分别表示两个归并段的首尾位置,brr[]为辅助数组,arr[]为存储结果的数组。
具体步骤:

实现过程:
(1) 自定义函数 merge(),实现一次归并排序。

(2) 自定义函数 merge_sort(),实现归并排序。

(3) 程序代码如下:

#include <stdio.h>

int merge(int r[], int s[], int x1, int x2, int x3) {   //自定义实现一次归并样序的函数

    int i, j, k;
    i = x1;    //第一部分的开始位置
    j = x2 + 1;  //第二部分的开始位置
    k = x1;
    while ((i <= x2) && (j <= x3))    //当i和j都在两个要合并的部分中时
        if (r[i] <= r[j])    //筛选两部分中较小的元素放到数组s中
        {
            s[k] = r[i];
            i++;
            k++;
        } else {
            s[k] = r[j];
            j++;
            k++;
        }
    while (i <= x2)    //将x1〜x2范围内未比较的数顺次加到数组r中
        s[k++] = r[i++];
    while (j <= x3) //将x2+l〜x3范围内未比较的数顺次加到数组r中
        s[k++] = r[j++];
    return 0;
}

int merge_sort(int r[], int s[], int m, int n) {
    int p;
    int t[20];
    if (m == n)
        s[m] = r[m];
    else {
        p = (m + n) / 2;
        merge_sort(r, t, m, p);    //递归调用merge_soit()函数将r[m]〜r[p]归并成有序的t[m]〜t[p]
        merge_sort(r, t, p + 1, n);    //递归一调用merge_sort()函数将r[p+l]〜r[n]归并成有序的t[p+l]〜t[n]
        merge(t, s, m, p, n);    //调用函数将前两部分归并到s[m]〜s[n】*/
    }
    return 0;
}

int main(void) {
    int a[11];
    int i;
    printf("请输入10个数:\n");
    for (i = 1; i <= 10; i++)
        scanf("%d", &a[i]);    //从键盘中输入10个数
    merge_sort(a, a, 1, 10);    //调用merge_sort()函数进行归并排序
    printf("排序后的顺序是:\n");
    for (i = 1; i <= 10; i++)
        printf("%5d", a[i]);    //输出排序后的数据
    printf("\n");

    return 0;
}

输出结果:

请输入10个数:
695 458 362 789 12 15 163 23 2 986
排序后的顺序是:
    8   12   15   23  163  362  458  695  789  986

** 方法二 **

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

void Merge(int *arr,int len,int gap)
{
    int *brr = (int *)malloc(sizeof(int) * len);  //动态开辟辅助数组
    assert(brr != NULL);
    int i = 0;           //brr的下标
    int start1 = 0;
    int end1 = start1+gap-1;
    int start2 = end1+1;
    int end2 = start2 + gap - 1 < len - 1 ? start2 + gap - 1 : len - 1 ;     //当有两个归并段的时候
    while(start2 < len)
    {
        //当两个归并段还没有比较完的时候
        while(start1 <= end1 && start2 <= end2)
        {
            if(arr[start1] <= arr[start2])
            {
                brr[i++] = arr[start1++];
            }
            else
            {
                brr[i++] = arr[start2++];
            }
        }
        while(start1 <= end1)     //如果是第二个归并段比较完了,则将第一个归并段中的数放在brr[]
        {
            brr[i++] = arr[start1++];
        }
        while(start2 <= end2)  //如果是第一个归并段比较完了,则将第二个归并段中的数放在brr[]
        {
            brr[i++] = arr[start2++];
        }
        //找两个新的归并段
        start1 = end2+1;
        end1 = start1+gap-1;
        start2 = end1+1;
        end2 = start2+gap-1 < len-1?start2+gap-1:len-1;
    }
    while(start1 < len)  //如果在最后不存在第二个归并段,则将第一个归并段中的数放在brr[]
    {
        brr[i++] = arr[start1++];
    }
    for(int i = 0;i < len;i++)    //数值拷贝
    {
        arr[i] = brr[i];
    }
}
void MergeSort(int *arr,int len)
{
    for(int i = 1;i < len;i *= 2)   //i表示分组数,分组形式为1 2 4 8....
    {
        Merge(arr,len,i);
    }
}

int main(void) {
    int a[11];
    int i;
    printf("请输入10个数:\n");
    for (i = 1; i <= 10; i++)
        scanf("%d", &a[i]);    //从键盘中输入10个数
    MergeSort(a,10);    //调用merge_sort()函数进行归并排序
    printf("排序后的顺序是:\n");
    for (i = 1; i <= 10; i++)
        printf("%5d", a[i]);    //输出排序后的数据
    printf("\n");

    return 0;
}

输出结果:

请输入10个数:
695 458 362 789 12 15 163 23 2 986
排序后的顺序是:
    8   12   15   23  163  362  458  695  789  986

技术要点:

归并是将两个或多个存序记录序列合并成一个有序序列。归并方法有多种,一次对两个有序记录序列进行归并,称为路归并排序,也有三路归并排序及多路归并排序。本实例是二路归并排序,基本方法如下:

(1) 将 n 个记录看成是 n 个长度为 1 的有序子表。

(2) 将两两相邻时有序无表进行归并。

(3) 重复执行步骤 (2) 直到归并成一个长度为 n 的有序表。