排序算法 | 歸併排序算法原理及實現和優化

歸併排序(MERGE-SORT)是建立在歸併操作上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。

歸併排序的原理


歸併排序的實現思想是:先將所有的記錄完全分開,然後兩兩合併,在合併的過程中將其排好序並且將已有序的子序列合併,最終能夠得到一個完整的有序表。

例如對於含有 n 個記錄的無序表,首先默認表中每個記錄各爲一個有序表(只不過表的長度都爲 1),然後進行兩兩合併,使 n 個有序表變爲 ⌈n/2⌉ 個長度爲 2 或者 1 的有序表(例如 4 個小有序表合併爲 2 個大的有序表),通過不斷地進行兩兩合併,直到得到一個長度爲 n 的有序表爲止。這種歸併排序方法稱爲:2-路歸併排序

例如對無序表{49,38,65,97,76,13,27}進行 2-路歸併排序的過程如圖 1 所示:

圖 1 歸併排序過程


歸併過程中,每次得到的新的子表本身有序,所以最終得到的爲有序表。

歸併排序的實現


public class MergeSort {

    public static void main(String[] args) {
        int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序數組
        mergeSort(array, 0, array.length - 1);
        print(array);
    }

    /**
     * 歸併排序
     * @param a 待排序的數組
     * @param s 數組的起始地址
     * @param t 數組的結束地址
     */
    public static void mergeSort(int[] a, int s, int t) {
        if(s >= t) //遞歸的出口
            return ;

        int m = (t + s)/2;       //每次遞歸將記錄表中記錄平分,直至每個記錄各成一張表
        mergeSort(a, s, m);      // 將分開的前半部分表中的記錄進行排序(a[s...m])
        mergeSort(a, m+1, t); // 將後半部分表中的記錄進行歸併排序(a[m+1...t])

        // a[s...m] 和 a[m...t]是兩個有序空間,
        // 將它們排序成一個有序空間a[s...t]
        merge(a, s, m, t);
    }

    /**
     * a數組中的記錄分成兩部分:下標從 s 至 m 有序,從 m+1 至 t 也有序,此函數的功能是合二爲一至temp數組中,使整個記錄表有序。
     * 二路歸併。原理:將兩個有序表合併和一個有序表
     * @param a 包含兩個有序區間的數組
     * @param s 第一個有序表的起始下標
     * @param m 第一個有序表的結束下標
     * @param t 第二個有序表的結束下標
     */
    private static void merge(int[] a, int s, int m, int t) {
        int[] tmp = new int[t - s + 1];// tmp是彙總2個有序區的臨時區域
        int i = s;  // 第1個有序區的索引
        int j = m + 1;  // 第2個有序區的索引
        int k = 0;  // 臨時區域的索引

        //將a數組中的兩部分記錄按照從小到大的順序添加至temp數組中
        while (i <= m && j <= t) {
            if (a[i] <= a[j]) {
                tmp[k++] = a[i++];
            } else {
                tmp[k++] = a[j++];
            }
        }
        // 將剩餘的比目前temp數組中都大的記錄複製到temp數組的最後位置
        while (i <= m) {
            tmp[k++] = a[i++];
        }
        while (j <= t) {
            tmp[k++] = a[j++];
        }
        // 將排序後的元素,全部都整合到數組a中
        System.arraycopy(tmp, 0, a, s, tmp.length);
    }

    /** 打印數組 */
    public static void print(int array[]) {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + "   ");
        }
        System.out.println();
    }
}

提示:歸併排序算法在具體實現時,首先需要將整個記錄表進行折半分解,直到分解爲一個記錄作爲單獨的一張表爲止,然後在進行兩兩合併。整個過程爲分而後立的過程。

歸併排序的特點及性能


歸併排序算法的時間複雜度爲O(nlogn)。該算法相比於堆排序和快速排序,其主要的優點是:當記錄表中含有值相同的記錄時,排序前和排序後在表中的相對位置不會改變,所以歸併排序是穩定的算法

例如,在記錄表中記錄 a 在記錄 b 的前面(記錄 a 和 b 的關鍵字的值相等),使用歸併排序之後記錄 a 還在記錄 b 的前面。這就體現出了該排序算法的穩定性。而堆排序和快速排序都是不穩定的。

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