1. 程式人生 > >演算法學習筆記--歸併排序

演算法學習筆記--歸併排序

歸併排序:一種簡單的利用遞迴排序的演算法,將一個數組先(遞迴地)將它分成兩半分別排序,然後將兩半分別排序,然後將結果歸併起來。

原地歸併的抽象方法:

public static void merge(int[] a, int lo, int mid, int hi){
        //將a[lo..mid] 和 a[mid+1..hi] 歸併
        int N = a.length;
        int[] b = new int[N];
        int i = lo;
        int j = mid + 1;
        for (int k = lo; k <= hi; k++) {
            b[k] = a[k];
        }
        //歸併回a[lo..hi]
for (int k = lo; k <= hi; k++) { if(i > mid){ a[k] = b[j++]; }else if(j > hi){ a[k] = b[i++]; } else if(b[i] < b[j]){ a[k] = b[i++]; }else{ a[k] = b[j++]; } } }

先將所有元素複製到吧b[]中,然後再歸併回a[]中。


歸併軌跡如下圖:

這裡寫圖片描述
這裡寫圖片描述


自頂向下的歸併排序:

這是基於原地歸併的抽象實現的遞迴歸併,應用了高效演算法設計中的分治思想的典型的例子。

public static void sort(int[] a, int lo, int hi){    // lo,hi 陣列下標
        if(hi <= lo){
            return;
        }
        int mid = lo + (hi-lo)/2;
        sort(a, lo, mid);       //將左半邊排序
        sort(a, mid+1
, hi); //將右半邊排序 Merge.merge(a, lo, mid, hi); //原地歸併的抽象方法 }

要對陣列 a[lo..hi] 進行排序,先將它分為a[lo..mid] 和 a[mid+1..hi] 兩部分,分別通過遞迴呼叫將它們單獨排序,最後將有序的子陣列歸併為最終的排序結果。

歸併結果軌跡:

這裡寫圖片描述



自底向上的歸併排序:

這種排序的思想是:先歸併那些微型陣列,然後再成對歸併得到的子陣列。
先進行兩兩歸併,然後再四四歸併,再八八歸併。。。

public static void sort(int[] a){
        int N = a.length;
        for (int sz = 1; sz < N; sz = 2*sz) {     //子陣列的大小
            for (int lo = 0; lo < N - sz; lo += 2*sz) {   // lo為子陣列的索引
                Merge.merge(a, lo, lo + sz - 1, Math.min(N - 1, lo + 2*sz -1));
            }
        }
    }


歸併軌跡圖:

這裡寫圖片描述

特點:

對於長度為N的任意陣列,歸併排序需要(1/2)N/lgN至NlgN次比較,最多需要訪問陣列6NlgN次。
可以用歸併排序處理數百萬甚至更大規模的陣列。