【 声明:版权所有,欢迎转载。  联系信箱:yiluohuanghun@gmail.com】

   归并排序(Merge sort)的基本思想是合并两个有序表,设有两个有序(升序)序列存储在同一数组中相邻的位置上,不妨设为A[l..m],A[m+1..h],将它们归并为一个有序数列,并存储在A[l..h]。

  归并排序的时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlog2n),空间复杂度为O(N),它是一种稳定的排序方法。归并排序在最坏的情况下都是O(NlogN)的时间复杂度,缺点是Merge的时候要有O(N)的额外的空间.

   整体来说,对于归并排序可以运用分而治之方法来解决排序问题,该问题是将n 个元素排成非递减顺序。分而治之方法通常用以下的步骤来进行排序算法:若n 为1,算法终止;否则,将这一元素集合分割成两个或更多个子集合,对每一个子集合分别排序,然后将排好序的子集合归并为一个集合。

   假设仅将n 个元素的集合分成两个子集合。现在需要确定如何进行子集合的划分。一种可能性就是把前面n- 1个元素放到第一个子集中(称为A),最后一个元素放到第二个子集里(称为B)。按照这种方式对A递归地进行排序。由于B仅含一个元素,所以它已经排序完毕,在A排完序后,只需要用程序中的函数insert将A和B合并起来。把这种排序算法与InsertionSort进行比较,可以发现这种排序算法实际上就是插入排序的递归算法。该算法的复杂性为O(n2)。把n个元素划分成两个子集合的另一种方法是将含有最大值的元素放入B,剩下的放入A中。然后A被递归排序。为了合并排序后的A和B,只需要将B添加到A中即可。假如用函数M a x来找出最大元素,这种排序算法实际上就是SelectionSort的递归算法。

204541604.png

分治思想:

  将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解建立原问题的解,归并排序完全遵循分治模式:

分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列

解决:使用归并排序递归地排序两个子序列

合并:合并两个已排序的子序列以产生已排序的答案

归并排序算法:

  归并排序算法的关键操作是“合并”步骤中两个已排序序列的合并。我们通过调用一个辅助过程merge(A, p, q, r, T)来完成合并,其中A是一个数组,p,q,r是数组下标,满足p <= q < r, T是一个辅助数组空间。该过程假设子数组A[p..q]和A[q+1..r]都已排好序。它合并这两个子数组形成单一的已排好的子数组并代替当前的子数组A[p..r]n

过程merge需要O(n)的时间,其中n = r – p + 1是待合并元素的总数。

//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
    int i = first, j = mid + 1;
    int m = mid,   n = last;
    int k = 0;
    //逐个进行排序,并将排序结果保存在对应的临时数组中
    while (i <= m && j <= n)
    {
        if (a[i] <= a[j])
            temp[k++] = a[i++];
        else
            temp[k++] = a[j++];
    }
    //后半部分已遍历完,对前半部分剩下的直接拷贝
    while (i <= m)
        temp[k++] = a[i++];
    //前半部分已遍历完,对后半部分剩下的直接拷贝
    while (j <= n)
        temp[k++] = a[j++];
    //将存放在临时数组中的数据拷贝到原数组中
    for (i = 0; i < k; i++)
        a[first + i] = temp[i];
}

   现在,我们可以过程merge作为归并排序算法中的一个子程序来用。下面的过程mereg_sort(A, p, r)排序子数组A[p...r]中的元素。若p >= r,则该子数组最多有一个元素,所以已经排好序。否则,分解步骤简单地计算一个下标q,将A[p...r]分成两个子数组A[p...q]和A[q...r],前者包含n / 2个元素,后者包含n / 2个元素。

void mergesort(int a[], int first, int last, int temp[])
{
    if (first < last)
    {
        int mid = (first + last) / 2;
        mergesort(a, first, mid, temp);     //对前半部分进行排序
        mergesort(a, mid + 1, last, temp);  //对后半部分进行排序
        mergearray(a, first, mid, last, temp);  //合并前后两部分
    }
}
int MergeSort(int a[], int n)
{
    int *p = new int[n];
    if (p == NULL)
        return FALSE;
    mergesort(a, 0, n - 1, p);
    delete[] p;
    return TRUE;
}

   当然了,每到这个时候我都会说一样的话,测试程序一定要写,在程序可控范围内一定要将测试程序补上。

int main(int argc, char* argv[])
{
    int i, array[] = {6, 2, 3, 1, 4, 5};
    MergeSort(array, 6);
    for (i = 0; i < 6; i++)
    {
        printf("%d\t", array[i]);
    }
    getchar();
    return 0;
}

   下一篇貌似该堆排序了,但是对于堆排序我一直不是太懂,大致思路知道,但就是不太理解实际机制,过段时间在写吧。