1. 程式人生 > >資料結構和演算法 | 歸併排序演算法原理及實現和優化

資料結構和演算法 | 歸併排序演算法原理及實現和優化

歸併排序(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 的前面。這就體現出了該排序演算法的穩定性。而堆排序和快速排序都是不穩定的。