归并排序

 

归并排序

 

归并排序

1 归并排序

 

1.1 原理

归并排序是Divide and Conquer思想,也就是分而治之。先看划分的子问题。

假设对已经存在并且有序的两个序列A、B进行合并,如何进行?很显然可以新建一个序列C,长度是A与B长度之和,从左至右依次扫描A、B,将较小的项放入C直到A、B中所有元素都扫描结束即可。

1.2 实现

对于给定的数组A,A[p..r]与A[r+1..q]两部分均已有序,下面实现对此两部分合并,使A[p..q]最终有序。

    void merge(int A[],int p,int r,int q)
    {
        int i,j,k;
        int n1=r-p+1;
        int n2=q-r;
        int* L = (int*)malloc((n1+1)*sizeof(int));
        int* R = (int*)malloc((n2+1)*sizeof(int));
        i=j=0;
        while(i<n1)
        {
            L[i]=A[p+i];
            i++;
        }
        L[i] = MAXNUM;

        while(j<n2)
        {
            R[j] = A[r+j+1];
            j++;
        }
        R[j] = MAXNUM;
        i=0;
        j=0;
        for(k=p;k<=q;k++)
        {
            if(L[i]<=R[j])
            {
                A[k]=L[i];
                i++;
            }
            else
            {
                A[k]=R[j];
                j++;
            }
        }
        free(L);
        free(R);
    }
    

代码中申请数组时多申请一个int的尺寸,用来存放哨兵,这个值为正无穷大,这样即不必检查两个数组的边界,并且会正确把所有元素插入A中,因为任何数都小于正无穷大。 接下去的问题是怎样把一个无序数组来使用上面的方面排序。一个数组分成A[p..r]与A[r+1..q]两部分,对于这两部分再进行划分,以此重复,直到划分的两个子序列已经有序即可。代码实现如下:

    void mergeSort(int A[],int start,int end)
    {
        int mid=(start+end)/2;
        if(end > start)
        {
            mergeSort(A,start,mid);
            mergeSort(A,mid+1,end);
            merge(A,start,mid,end);
        }
    }
    

这是一个bottom-up的问题,它的表现是一棵二叉树,在最底层的序列只包含一个数,很明显单独的数不必进行排序。从最底层依次向上合并到顶点,整个过程也随之结束。

1.3 性能评估

上面提到,它的表现是一棵二叉树,而二叉树的遍历则需要花费O(nlgn)的时间复杂度。具体证明可以参见算法导论。

归并的思想是分治,先把复杂问题分解成可以解决的小问题,再把小问题解决,最后把解决的问题做合并,这样就解决整个问题。 而插入排序的思想是增量(incremental)策略,一点一点增加已排序的数组,直到排序全部完成。两个思想正好相反。

Date: 2013-05-18 20:43:37 中国标准时间

Author: 月窟仙人

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0
posted @ 2013-05-18 20:44  月窟仙人  阅读(122)  评论(0编辑  收藏  举报