排序演算法:歸併排序

ZealotZZZ發表於2018-07-18

Sorting Algorithms:Merge Sort

前言

該部落格用於本弱雞複習鞏固,打牢基礎,還望各大佬不吝賜教。

基本思路

歸併排序(二路歸併排序)
利用遞迴思和分治想,將原序列從中間分割成兩個子序列,
對分割好的子序列進行重複操作,
最後將拆分好的子序列進行排序,形成新的子序列,然後對子序列進行合併
形成排好序的新序列。

動圖示例

Merge Sort
Merge Sort

演算法複雜度分析

平均 最壞 最好 穩定性 空間複雜度
O(nlogn) O(nlogn) O(nlogn) 穩定 O(n+logn)

p.s.
n個元素都要被遍歷一遍以保證其被放到newArray,
需要將待排序序列中的所有記錄掃描一遍,所以O(n)。
由完全二叉樹可知,整個歸併排序需要 log2n次,
所以 最好=最壞=平均=O(n*logn)。
由於歸併排序需要與原始記錄序列同樣數量的儲存空間存放歸併結果以及遞迴時log2n的棧空間,
所以空間複雜度O(n+logn)。

程式碼實現

import java.util.Arrays;
import java.util.Random;

/**
 * MergingSort
 * 歸併排序(二路歸併排序)
 * 利用遞迴思和分治想,將原序列從中間分割成兩個子序列,
 * 對分割好的子序列進行重複操作,
 * 最後將拆分好的子序列進行排序,形成新的子序列,然後對子序列進行合併
 * 形成排好序的新序列
 * 時間複雜度分析
 * n個元素都要被遍歷一遍以保證其被放到newArray,
 * 需要將待排序序列中的所有記錄掃描一遍,O(n)
 * 由完全二叉樹可知,整個歸併排序需要 log2n次
 * 所以 最好=最壞=平均=O(n*logn)
 * 由於歸併排序需要與原始記錄序列同樣數量的儲存空間存放歸併結果以及遞迴時log2n的棧空間,
 * 空間複雜度O(n+logn)
 * 穩定性 穩定
 */

public class MergingSort {
    public static void main(String[] args) {
        int[] a = new int[10];
        boolean flag = true;
        //random array
        for (int i = 0; i < a.length; i++) {
            Random rd = new Random();
            a[i] = rd.nextInt(10);
        }

        System.out.println("Random Array :");
        System.out.println(Arrays.toString(a));
        System.out.println();
        System.out.println("Merging Sort :");

        //歸併排序開始
        mergingSort(a, a, 0, a.length - 1);

        System.out.println(Arrays.toString(a));
    }

    //遞迴方法,實現對序列的分割和合並
    //傳入的是待排序列,要生成的新序列,序列開始和結尾角標
    public static void mergingSort(int[] oldArray, int[] newArray, int start, int end) {
        //定義該值將序列從中間分割
        int mid;
        //定義一個臨時陣列容納被分割的陣列
        int[] tempArray = new int[oldArray.length];
        //如果陣列頭角標==尾角標,則序列待排僅一個元素,新序列就是舊序列
        //推理可知,遞迴進行到最後,序列一定會被分割成單個元素
        if (start == end) {
            newArray[start] = oldArray[start];
        } else {
            //取中值
            mid = (start + end) / 2;
            //分別對新分割好的序列進行分割
            mergingSort(oldArray, tempArray, start, mid);
            mergingSort(oldArray, tempArray, mid + 1, end);
            //對分割好的序列進行排序和合並操作
            merge(tempArray, newArray, start, mid, end);

        }
    }

    public static void merge(int[] tempArray, int[] newArray, int start, int mid, int end) {
        int j, k, l;
        //已知每個序列被分割成了兩個序列,左和右序列
        //從左和右序列的最小角標開始,依次進行比較
        for (j = mid + 1, k = start; start <= mid && j <= end; k++) {
            //temp[i]到temp[mid]左序列 temp[mid+1]到temp[end]右序列
            //左序列當前元素小,就先放到新序列裡 反之,右序列當前元素先放入
            //該句元素比較程式碼說明了其是穩定的
            if (tempArray[start] < tempArray[j]) {
                newArray[k] = tempArray[start++];
            } else {
                newArray[k] = tempArray[j++];
            }
        }
        //上個操作完成後,左或右序列可能有剩餘,繼續講剩餘元素補充到新序列中
        if (start <= mid) {
            for (l = 0; l <= mid - start; l++) {
                newArray[k + l] = tempArray[start + l];
            }
        }
        if (j <= end) {
            for (l = 0; l <= end - j; l++) {
                newArray[k + l] = tempArray[j + l];
            }
        }
    }
}
複製程式碼

參考

GeeksforGeeks:https://www.geeksforgeeks.org/merge-sort/

十大經典排序演算法:https://www.cnblogs.com/onepixel/articles/7674659.html

《大話資料結構》:https://book.douban.com/subject/6424904/

相關文章