1. 程式人生 > >歸併排序詳述(程式碼+圖解)

歸併排序詳述(程式碼+圖解)

我是看了這一篇部落格的圖+一篇部落格的程式碼看明白的~~現在把這兩個合在一起了,感謝博主 


以下轉載自->部落格連結

圖解排序演算法(四)之歸併排序

基本思想

  歸併排序(MERGE-SORT)是利用歸併的思想實現的排序方法,該演算法採用經典的分治(divide-and-conquer)策略(分治法將問題(divide)成一些小的問題然後遞迴求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。

分而治之

   可以看到這種結構很像一棵完全二叉樹,本文的歸併排序我們採用遞迴去實現(也可採用迭代的方式去實現)。

階段可以理解為就是遞迴拆分子序列的過程,遞迴深度為log2n。

合併相鄰有序子序列

  再來看看階段,我們需要將兩個已經有序的子序列合併成一個有序序列,比如上圖中的最後一次合併,要將[4,5,7,8]和[1,2,3,6]兩個已經有序的子序列,合併為最終序列[1,2,3,4,5,6,7,8],來看下實現步驟。

以下轉載自->部落格連結

 

/**
 * 歸併排序:C 語言
 *
 * @author skywang
 * @date 2014/03/12
 */

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

// 陣列長度
#define LENGTH(array) ( (sizeof(array)) / (sizeof(array[0])) )

/*
 * 將一個數組中的兩個相鄰有序區間合併成一個
 *
 * 引數說明:
 *     a -- 包含兩個有序區間的陣列
 *     start -- 第1個有序區間的起始地址。
 *     mid   -- 第1個有序區間的結束地址。也是第2個有序區間的起始地址。
 *     end   -- 第2個有序區間的結束地址。
 */
void merge(int a[], int start, int mid, int end)
{
    int *tmp = (int *)malloc((end-start+1)*sizeof(int));    // tmp是彙總2個有序區的臨時區域
    int i = start;            // 第1個有序區的索引
    int j = mid + 1;        // 第2個有序區的索引
    int k = 0;                // 臨時區域的索引

    while(i <= mid && j <= end)
    {
        if (a[i] <= a[j])
            tmp[k++] = a[i++];
        else
            tmp[k++] = a[j++];
    }

    while(i <= mid)
        tmp[k++] = a[i++];

    while(j <= end)
        tmp[k++] = a[j++];

    // 將排序後的元素,全部都整合到陣列a中。
    for (i = 0; i < k; i++)
        a[start + i] = tmp[i];

    free(tmp);
}

/*
 * 歸併排序(從上往下)
 *
 * 引數說明:
 *     a -- 待排序的陣列
 *     start -- 陣列的起始地址
 *     endi -- 陣列的結束地址
 */
void merge_sort_up2down(int a[], int start, int end)
{
    if(a==NULL || start >= end)
        return ;

    int mid = (end + start)/2;
    merge_sort_up2down(a, start, mid); // 遞迴排序a[start...mid]
    merge_sort_up2down(a, mid+1, end); // 遞迴排序a[mid+1...end]

    // a[start...mid] 和 a[mid...end]是兩個有序空間,
    // 將它們排序成一個有序空間a[start...end]
    merge(a, start, mid, end);
}


/*
 * 對陣列a做若干次合併:陣列a的總長度為len,將它分為若干個長度為gap的子陣列;
 *             將"每2個相鄰的子陣列" 進行合併排序。
 *
 * 引數說明:
 *     a -- 待排序的陣列
 *     len -- 陣列的長度
 *     gap -- 子陣列的長度
 */
void merge_groups(int a[], int len, int gap)
{
    int i;
    int twolen = 2 * gap;    // 兩個相鄰的子陣列的長度

    // 將"每2個相鄰的子陣列" 進行合併排序。
    for(i = 0; i+2*gap-1 < len; i+=(2*gap))
    {
        merge(a, i, i+gap-1, i+2*gap-1);
    }

    // 若 i+gap-1 < len-1,則剩餘一個子陣列沒有配對。
    // 將該子數組合併到已排序的陣列中。
    if ( i+gap-1 < len-1)
    {
        merge(a, i, i + gap - 1, len - 1);
    }
}

/*
 * 歸併排序(從下往上)
 *
 * 引數說明:
 *     a -- 待排序的陣列
 *     len -- 陣列的長度
 */
void merge_sort_down2up(int a[], int len)
{
    int n;

    if (a==NULL || len<=0)
        return ;

    for(n = 1; n < len; n*=2)
        merge_groups(a, len, n);
}

void main()
{
    int i;
    int a[] = {80,30,60,40,20,10,50,70};
    int ilen = LENGTH(a);

    printf("before sort:");
    for (i=0; i<ilen; i++)
        printf("%d ", a[i]);
    printf("\n");

    merge_sort_up2down(a, 0, ilen-1);        // 歸併排序(從上往下)
    //merge_sort_down2up(a, ilen);            // 歸併排序(從下往上)

    printf("after  sort:");
    for (i=0; i<ilen; i++)
        printf("%d ", a[i]);
    printf("\n");
}