1. 程式人生 > 其它 >排序:歸併排序

排序:歸併排序

簡介

歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序 表,稱為二路歸併。

排序原理:

1.儘可能的一組資料拆分成兩個元素相等的子組,並對每一個子組繼續拆分,直到拆分後的每個子組的元素個數是1為止。

2.將相鄰的兩個子組進行合併成一個有序的大組;

3.不斷的重複步驟2,直到最終只有一個組為止。

自頂而下的歸併排序程式碼實現:

/**
 * @author wen.jie
 * @date 2021/8/5 15:46
 */
public abstract class AbstractSort {

    /**
     * 比較
     * @author wen.jie
     * @date 2021/8/4 17:18
     */
    protected static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }

    /**
     * v是否比w小
     * @author wen.jie
     * @date 2021/8/5 15:59
     */
    protected static boolean less(Comparable v, Comparable w){
        return greater(w, v);
    }

    /**
     * 交換
     * @author wen.jie
     * @date 2021/8/4 17:27
     */
    protected static void exchange(Comparable[] a, int i, int j) {
        Comparable temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

/**
 * @author wen.jie
 * @date 2021/8/5 16:13
 */
public class Merge extends AbstractSort{

    private static Comparable[] assist;

    public static void sort(Comparable[] a){
        assist = new Comparable[a.length];
        sort(a, 0, a.length - 1);
    }

    /**
     * 排序:從lo到hi的元素
     * @author wen.jie
     * @date 2021/8/5 16:17
     */
    public static void sort(Comparable[] a, int lo, int hi){
        if(hi <= lo) return;
        int mid  = lo + (hi - lo) / 2;
        //兩組分別排序
        sort(a, lo, mid);
        sort(a, mid + 1, hi);
        //兩組中的資料進行合併
        merge(a, lo, mid, hi);
    }

    /**
     * 合併兩個分組
     * @author wen.jie
     * @date 2021/8/5 16:21
     */
    public static void merge(Comparable[] a, int lo, int mid, int hi){
        //定義兩個指標,對應著兩個子陣列
        int i = lo, j = mid + 1;
        //將所有元素先複製到臨時陣列中
        System.arraycopy(a, lo, assist, lo, hi + 1 - lo);
        //歸併到a[lo...hi]
        for (int k = lo; k <= hi; k++) {
            if(i > mid)
                //左半邊用盡,取右半邊元素
                a[k] = assist[j++];
            else if (j > hi)
                //右半邊用盡,取右半邊元素
                a[k] = assist[i++];
            else if (less(assist[j], assist[i]))
                //右邊比左邊小,取右邊元素
                a[k] = assist[j++];
            else
                //左邊比右邊小,取左邊元素
                a[k] = assist[i++];
        }
    }

}

自底而上的歸併排序:

這裡是先歸併微型陣列,再成對歸併得到的子陣列,如此這般,直到我們將整個陣列歸併到一起。

//只有sort方法不一樣,其他方法一模一樣    
public static void sort(Comparable[] a){
        int n = a.length;
        assist = new Comparable[n];
        for (int sz = 1; sz < n; sz = 2*sz) {
            for (int lo = 0; lo < n-sz; lo += 2*sz){
                merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, n-1));
            }
        }
    }

自頂而下時間複雜度分析

用樹狀圖來描述歸併,如果一個數組有8個元素,那麼它將每次除以2找最小的子陣列,共拆log8次,值為3,所以 樹共有3層,那麼自頂向下第k層有2^k個子陣列,每個陣列的長度為2^(3-k),歸併最多需要2^(3-k)次比較。因此每層 的比較次數為 2^k * 2^(3-k)=2^3,那麼3層總共為 3*2^3。

假設元素的個數為n,那麼使用歸併排序拆分的次數為log2(n),所以共log2(n)層,那麼使用log2(n)替換上面3*2^3中 的3這個層數,最終得出的歸併排序的時間複雜度為:log2(n)* 2^(log2(n))=log2(n)*n,根據大O推導法則,忽略底數,最終歸併排序的時間複雜度為O(nlogn);