1. 程式人生 > >Java 歸併排序法

Java 歸併排序法

一、原理簡介

歸併排序又稱合併排序,即針對兩個已經排序好的陣列,將其合併為一個排序陣列。歸併過程中,依次比較兩個陣列中的元素,將較小值(假設為升序排序)放入新陣列中,最終遍歷完兩個陣列後完成排序。

歸併排序是一種速度較快的排序方法。

二、時間複雜度

當我們合併 兩個已排序的陣列,只需要遍歷一遍即可,其時間複雜度為 O(n)。

當我們對一個 無序陣列 使用歸併排序法進行排序,我們可以使用分治法將陣列進行對半劃分,遞迴解決。由於進行二分的時間複雜度為 O(log n),單次合併(即合併兩個已排序的陣列)操作時間複雜度為 O(n),所以綜合時間複雜度為 O(n * log n)。

三、合併兩個已排序的陣列

這種情況時間複雜度為 O(n)。因為我們只需要遍歷一遍。

1. 兩個數組合併為一個

public class Main {
 
    public static void main(String[] args) {
        // 前提:arr1 和 arr2 都是已排序的
        int[] arr1 = new int[] { 3, 15, 23, 76, 100 };
        int[] arr2 = new int[] { 1, 16, 18, 55, 66 };
        int[] arr = mergeSort(arr1, arr2);
        printArray(arr);
    }
 
    public static int[] mergeSort(int[] arr1, int[] arr2) {
        int[] arr = new int[arr1.length + arr2.length];
        int i = 0; // arr1當前index
        int j = 0; // arr2當前index
        int k = 0; // arr當前index
        while (i < arr1.length && j < arr2.length) {
            if (arr1[i] < arr2[j]) {
                arr[k++] = arr1[i++];
            } else {
                arr[k++] = arr2[j++];
            }
        }
        if (i != arr1.length) {
            System.arraycopy(arr1, i, arr, k, arr1.length - i);
        } else {
            System.arraycopy(arr2, j, arr, k, arr2.length - j);
        }
        return arr;
    }
 
    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
 
}

2. 一個數組,左右兩段是已排序的

和上例類似,只是 arr1 和 arr2 可以視為這一個陣列的左右兩部分。

public class Main {
 
    public static void main(String[] args) {
        // 前提:arr 的左半部分[0, 5)和右半部分[5, 10)均已排序。區間均為左閉右開
        int[] arr = new int[] { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
        mergeSort(arr, 0, 5, 10);
        printArray(arr);
    }
 
    /**
     * 歸併排序
     * @param arr 目標陣列,滿足 p < q≤ r,且區間 [p...q) 和 [q...r) 均已排好序
     * @param p 起始位置
     * @param q 中間位置
     * @param r 結束位置
     */
    public static void mergeSort(int[] arr, int start, int pivot, int end) {
        int[] temp = new int[end - start];
        int i = start;
        int j = pivot;
        int k = 0;
        while (i < pivot && j < end) {
            if (arr[i] < arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
            }
        }
        if (i != pivot) {
            System.arraycopy(arr, i, temp, k, pivot - i);
        } else {
            System.arraycopy(arr, j, temp, k, end - j);
        }
        System.arraycopy(temp, 0, arr, start, temp.length);
    }
 
    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
 
}

四、無序陣列排序

這種情況時間複雜度為 O(n * log n)。

我們需要對陣列折半劃分,直至其足夠小時(例如陣列長度為 1),可視為左右兩個陣列是已排序的(長度為 1 的陣列可以視為已排序的),然後分別執行上面的合併操作。遞迴執行下去,最終完成整個陣列的排序。

public class Main {
 
    public static void main(String[] args) {
        int[] arr = new int[] { 2, 3, 8, 6, 1 };
        mergeSort(arr, 0, arr.length);
        printArray(arr);
    }
 
    public static void mergeSort(int[] arr, int start, int end) {
        int length = end - start;
        if (length > 1) { // 長度大於1才需要排序
            int mid = (start + end) / 2;
            mergeSort(arr, start, mid); // sort left
            mergeSort(arr, mid, end); // sort right
            merge(arr, start, mid, end); // merge left and right
        }
    }
 
    public static void merge(int[] arr, int start, int mid, int end) {
        // check input
        if (arr == null || start < 0 || end > arr.length) {
            return;
        }
        int[] temp = new int[end - start];
        int i = start;
        int j = mid;
        int k = 0;
        while (i < mid && j < end) {
            if (arr[i] <= arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
            }
        }
        if (i != mid) {
            System.arraycopy(arr, i, temp, k, mid - i);
        } 
        if (j != end){
            System.arraycopy(arr, j, temp, k, end - j);
        }
        System.arraycopy(temp, 0, arr, start, temp.length);
    }
 
    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
 
}