【算法與數據結構】分治策略與歸併排序

分治策略

對於所求的問題域,每次將其分爲大小相似的兩部分,然後再把每一部分當作最初的問題域再次分割,依次遞歸,直到得到基本問題(遞歸基),求解。然後,再逐步合併直到完成最初的問題域。

通常用遞歸的方式分割問題,例如這裏的歸併排序,每次將數組一分爲二,然後分別判斷分割動作是否到不可再分割了(數組中僅剩最後一個元素),否則繼續對左右兩個子數組進行分割:

void merge_sort(int arr[], int lo, int hi) {
    if (lo + 1 >= hi) {
        return;
    }
    int mi = (hi + lo) / 2;
    merge_sort(arr, lo, mi);
    merge_sort(arr, mi, hi);
    merge(arr, lo, mi, hi);
}

歸併排序的合併操作

每次要合併兩個子數組時,同時判斷兩個數組的開頭位置的元素大小,取小的元素,直到有一個數組爲空時,把非空數組的所有元素複製過來:

void merge(int arr[], int lo, int mi, int hi) {
    if (lo + 1 == hi) {
        return;
    }
    // 原數組 lo 至 hi 之間的元素已經有序,分別複製出來到兩個新數組
    int llen, rlen;
    int i, j, k;
    llen = mi - lo;
    rlen = hi - mi;
    int larr[llen];
    int rarr[rlen];
    for (i = 0; i < llen; i++) {
        larr[i] = arr[lo + i];
    }
    for (i = 0; i < rlen; i++) {
        rarr[i] = arr[mi + i];
    }
    // 依次判斷兩個新數組的開頭元素,把小的放到原數組
    i = 0;
    j = 0;
    k = lo;
    while(i < llen && j < rlen) {
        if (larr[i] <= rarr[j]) {
            arr[k++] = larr[i++];
        } else {
            arr[k++] = rarr[j++];
        }
    }
    // 新數組有一個爲空時,把另一個完整的複製到原數組
    while(i < llen) {
        arr[k++] = larr[i++];
    }
    while(j < rlen) {
        arr[k++] = rarr[j++];
    }
}

完整示例代碼

#include <stdio.h>

void merge(int arr[], int lo, int mi, int hi) {
    if (lo + 1 == hi) {
        return;
    }
    int llen, rlen;
    int i, j, k;
    llen = mi - lo;
    rlen = hi - mi;
    int larr[llen];
    int rarr[rlen];
    for (i = 0; i < llen; i++) {
        larr[i] = arr[lo + i];
    }
    for (i = 0; i < rlen; i++) {
        rarr[i] = arr[mi + i];
    }
    i = 0;
    j = 0;
    k = lo;
    while(i < llen && j < rlen) {
        if (larr[i] <= rarr[j]) {
            arr[k++] = larr[i++];
        } else {
            arr[k++] = rarr[j++];
        }
    }
    while(i < llen) {
        arr[k++] = larr[i++];
    }
    while(j < rlen) {
        arr[k++] = rarr[j++];
    }
}

void merge_sort(int arr[], int lo, int hi) {
    if (lo + 1 >= hi) {
        return;
    }
    int mi = (hi + lo) / 2;
    merge_sort(arr, lo, mi);
    merge_sort(arr, mi, hi);
    merge(arr, lo, mi, hi);
}

int main(void) {
    int arr[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
    int n = 10;
    int i;
    
    for (i = 0; i < n; i++) {
        printf("%4d", arr[i]);
    }
    printf("\n");
    merge_sort(arr, 0, n);
    
    for (i = 0; i < n; i++) {
        printf("%4d", arr[i]);
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章