歸並排序基本原理及實現


一、歸並(Merge)

1. 概念

將兩個有序數列合並成一個有序數列,我們稱之為“歸並”。

 

2. 算法思路及實現

設兩個有序的子序列(相當於輸入序列)放在同一序列中相鄰的位置上:array[low..m],array[m + 1..high],先將它們合並到一個局部的暫存序列 temp (相當於輸出序列)中,待合並完成后將 temp 復制回 array[low..high]中,從而完成排序。

在具體的合並過程中,設置 i,j 和 p 三個指針,其初值分別指向這三個記錄區的起始位置。合並時依次比較 array[i] 和 array[j] 的關鍵字,取關鍵字較小(或較大)的記錄復制到 temp[p] 中,然后將被復制記錄的指針 i 或 j 加 1,以及指向復制位置的指針 p 加 1。重復這一過程直至兩個輸入的子序列有一個已全部復制完畢(不妨稱其為空),此時將另一非空的子序列中剩余記錄依次復制到 array 中即可。

c語言實現如下:

void merge(int* array, int low, int mid, int high)
{
    assert(array && low >= 0 && low <= mid && mid <= high);

    int* temp = (int*)malloc((high - low + 1) * sizeof(int));
    if (!temp) {
        printf("Error:out of memory!");
        return;
    }

    int i = low;
    int j = mid + 1;
    int index = 0;

    while (i <= mid && j <= high) {
        if (array[i] <= array[j]) {
            temp[index++] = array[i++];
        }
        else {
            temp[index++] = array[j++];
        }
    }

    while (i <= mid) {
        temp[index++] = array[i++];
    }

    while (j <= high) {
        temp[index++] = array[j++];
    }

    memcpy((void*)(array + low), (void*)temp, (high - low + 1) * sizeof(int)) ;

    free(temp);
}

 

二、 歸並排序(Merge Sort)概念

建立在歸並操作上的一種排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。

歸並排序有多路歸並排序、兩路歸並排序,可用與內排序,也可用於外排序,本文僅對內排序的兩路歸並方法進行討論。

 

三、歸並排序思路及實現

1. 從下往上的歸並排序

  • 把n個記錄看成n個長度為1的數列(因為長度為1,所以自然有序)。
  • 進行兩兩歸並,得到n/2個長度為2的有序數列,再將這些數列兩兩合並,得到n/4個長度為4的有序數列,重復這個過程,直到合並成一個數列為止。

過程如下圖所示:

merge-sort-animation

C語言實現:

// 對 [0, length - 1] 做一趟歸並長度為 n  的歸並排序
void merge_pass(int* array, int length, int n)
{
    assert(array && length >= 1 && n >= 1);

    int i;
    int sortLength = 2 * n;

    // 歸並長度為 n 的兩個相鄰子序列
    for(i = 0; i + sortLength - 1 < length; i = i + sortLength) {
        merge(array, i, i + n - 1, i + sortLength - 1);
    }

    // 若 i + n - 1 < length - 1,則剩余一個子文件輪空,無須歸並。
    // 尚有兩個子序列,其中后一個長度小於 n, 歸並最后兩個子序列。
    if (length - 1 > i + n - 1) {
        merge(array, i, i + n - 1, length - 1);
    }
}

// 用分治法自下向上進行二路歸並排序
//
void merge_sort(int* array, int length)
{
    assert(array && length >= 0);

    int n;

    for(n = 1; n < length; n = (n << 1)) {
        merge_pass(array, length, n);
    }
}

優點是效率高,缺點是代碼可讀性差。

 

2. 從上往下的歸並排序

具體的實現有三個步驟:

  • 分解 -- 將當前區間一分為二,即求分裂點 mid = (low + high)/2;
  • 求解 -- 遞歸地對兩個子區間a[low...mid] 和 a[mid+1...high]進行歸並排序。遞歸的終結條件是子區間長度為1。
  • 合並 -- 將已排序的兩個子區間a[low...mid]和 a[mid+1...high]歸並為一個有序的區間a[low...high]。

如下圖所示:

151853346211212

C語言實現:

void merge_sort_dc_impl(int* array, int low, int high)
{
    assert(array && low >= 0);

    int mid;
    if (low < high) {
        mid = (low + high) >> 1;

        merge_sort_dc_impl(array, low, mid);
        merge_sort_dc_impl(array, mid + 1, high);

        merge(array, low, mid, high);
    }
}

// 用分治法自上向下進行排序
void merge_sort_dc(int* array, int length)
{
    assert(array && length >= 0);

    merge_sort_dc_impl(array, 0, length - 1);
}

 

四、時間復雜度及穩定性

1. 時間復雜度

長度為n的序列需要進行logn次二路歸並才能完成排序(歸並排序的形式其實就是一棵二叉樹,需要遍歷的次數就是二叉樹的深度),而每趟歸並的時間復雜度為O(n),因此歸並排序的時間復雜度為O(nlogn)

 

2. 排序穩定性

所謂排序穩定性,是指如果在排序的序列中,存在兩個相等的兩個元素,排序前和排序后他們的相對位置不發生變化的話,我們就說這個排序算法是穩定的。

排序算法是穩定的算法。

 

五、參考

1. 排序算法之歸並排序

2. 歸並排序

3. 常見排序算法 - 歸並排序 (Merge Sort)

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM