归并排序

在介绍归并排序之前,首先需要介绍一下分治法的思想

很多算法在结构上是递归的,算法需要一次或多次调用自身来解决相应的子问题,即将原问题划分成n个规模较小而结构与原问题相似的子问题,递归的解决这些子问题,然后合并其结果,就得到原问题的解。

分解:将原问题分解为一系列子问题

解决:递归解决各个子问题。若子问题足够小,则直接解决

合并:将子问题结果合并成原问题的解。


归并排序其实就是应用了分治法的一个典型实例,直观的操作如下:

分解:将n个元素分成个含n/2个元素的子序列

解决:用归并排序递归的对子序列进行排序

合并:合并两个子序列得到排序结果

在对子序列进行排序时,其长度为1时递归结束,单个元素被视为是排好序的。

归并排序的关键步骤在于合并两个已经排好序的子序列。为做排序,引入一个辅助过程MERGE(A,p,q,r),A是数组,p,q,r是下标,满足p<=q<r,假设A[p..q]和A[q+1..r]都已经排好序了,将他们合并成一个子数组A[p..r]。

对于这个函数的工作过程,想象一下桌面上有两堆已经排好序的扑克牌,那么怎么讲这两堆牌合并成一堆呢?只需要每一次都从这两堆牌中取出较小的那一个放到一个新的扑克牌堆中(刚开始必然什么都没有),重复以上过程,知道有一堆牌空了,此时,只需要将剩下的那堆牌直接扔到新的堆中就可以了。



在具体的代码实现中,略微有些变化,为了避免检查每一个堆是否是空的,在每一个堆的底部都放上一张“哨兵牌”,包含了一个特殊值(自己设置,可以使用无穷大,目的就是为了区分)。当一个堆中出现了哨兵牌后,由于它的值大于所有的牌,每次选取的时候只能选择另一个堆中的牌,直到同时出现两张哨兵牌。当然,由于我们预先知道了会有r-p+1张牌会输出到堆中,所以一旦执行了r-p+1次后,算法就可以停下来了。

一下是具体实现代码:

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

//定义INF变量用于哨兵中,假设在一个大小为MAX的数组中实现归并排序
#define INF 10000
#define MAX 10

//三个参数A表示待排序的数组,其中,A[p..q]和A[q+1..r]已经排序完毕
void MERGE(int* A,int p,int q,int r)
{
    const int n1=q-p+1;const int n2=r-q;
    int L[n1+1];int R[n2+1];
    int i,j,k;
    for(i=0;i<n1;i++)
        L[i]=A[p+i];
    for(j=0;j<n2;j++)
        R[j]=A[q+j+1];
    L[n1]=INF;R[n2]=INF;
    i=j=0;
    //接下来就是讲两个堆合并成一个堆的过程,记住,k的取值范围是p到r(调这个错误花了老子一下午)
    for(k=p;k<=r;k++)
    {
        if(L[i]<=R[j])
        {
            A[k]=L[i];
            i++;
        }
        else
        {
            A[k]=R[j];
            j++;
        }
    }
}

//归并排序算法,分而治之
void Merge_Sort(int* A,int p,int r)
{
    int q;
    if(p<r)
    {
        q=(p+r)/2;
        Merge_Sort(A,p,q);
        Merge_Sort(A,q+1,r);
        MERGE(A,p,q,r);
    }
}

//主函数测试数据
int main()
{
    int count;
    int A[MAX]={2,41,6,18,10,12,33,5,7,91};
    Merge_Sort(A,0,9);
    for(count=0;count<MAX;count++)
        printf("%d ",A[count]);
    printf("\n");
    return 0;
}


发布了35 篇原创文章 · 获赞 35 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章