排序:歸併排序
阿新 • • 發佈:2021-08-05
簡介
歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序 表,稱為二路歸併。
排序原理:
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);