1. 程式人生 > >God Liao On The Way

God Liao On The Way

歸併排序的流程如下

第一步:對數列每次都進行切分,直到不能再切分
這裡寫圖片描述

每次都對數列進行切分分組,直到每組的元素都只有一個,學過遞迴的話很容易想到這種重複性的動作用遞迴很容易實現。對於8個數的數列,切分三次直到第四層就不能再切分了。

第二步:進行歸併

對於上述的數列,當每組的元素都是一個,也就是說每組的都是有序的了,遞迴返回,返回到倒數第二層。此時可對每組元素進行歸併

這裡寫圖片描述

再進行歸併

這裡寫圖片描述

可以看到歸併將每組的數排成有序的了,最後我們將最後兩組數再進行歸併即可。

這裡寫圖片描述

此時,數列有序,歸併結束

程式設計思路

對於每次把數列按半切分分組,對分好的組進行歸併這個流程可以用遞迴實現:

//歸併過程
    public static void merge(int arr[],int l,int mid,int r){
        //歸併排序輔助陣列
        int T[] = new int[r-l+1];
        //T[0]到T[r-l]儲存arr[l]到arr[r]的值
        for(int i = l; i <= r; i++){
            T[i-l] = arr[i];
        }
        //使用i指向分組1的第一個數的位置,j指向分組2的第一個數的索引
        int i = l,j = mid+1
; //需要歸併的陣列位置是[l,r] for(int k = l ; k <= r; k++){ if(i > mid){ //歸併後,分組2還有元素,依次覆蓋到原陣列對應處 arr[k] = T[j-l]; j++; }else if(j > r){ //歸併後,分組1還有元素,依次覆蓋到原陣列對應處 arr[k] = T[i-l]; i++; }else
if(T[i-l] < T[j-l]){ //將分組中小的數覆蓋到原陣列對應處 arr[k] = T[i-l]; i++; }else if(T[i-l] >= T[j-l]){ arr[k] = T[j-l]; j++; } } }

T[]是輔助陣列,用來存放兩個需要進行歸併的分組。在歸併時,每次將兩組中的最小的那個數依次放到原陣列。

這個過程如下所示:
這裡寫圖片描述

優化

public static void merge_sort(int arr[],int l,int r){
        if(l >= r)
            return;
        /**對於數不是很多的情況下,可以使用插入排序代替歸併來提高效率
         * if(r - l <= 10){
         *      插入排序;
         *      return;
         * }
        */
        int mid = (l+r) / 2;
        merge_sort(arr,l,mid);
        merge_sort(arr,mid+1,r);
        //優化,歸併時左邊的最後一個數已經是小於右邊第一個數時,可以不用歸併了
        if(arr[mid] > arr[mid+1]){
            merge(arr,l,mid,r);
        }
    }