數據結構與算法系列——排序(10)_歸併排序 圖解排序算法(四)之歸併排序 歸併排序

1. 工做原理(定義)

歸併排序(Merge sort)是創建在歸併操做上的一種有效的排序算法,指的是將兩個已經排序的序列合併成一個序列的操做。該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。html

做爲一種典型的分而治之思想的算法應用,歸併排序的實現由兩種方法:git

  • 自上而下的遞歸(全部遞歸的方法均可以用迭代重寫,因此就有了第 2 種方法);
  • 自下而上的迭代;

2. 算法步驟

1. 從下往上的歸併排序【迭代】:github

  1. 經過分治法將長度爲n的序列劃分爲n個長度爲1的子序列。 算法

  2. 進行兩兩歸併比較,獲得 n/2 個長度爲 2 的有序子序列 數組

  3. 重複第 2 步,直到全部子序列歸併成一個長度爲 n 的有序序列。數據結構

2. 從上往下的歸併排序【遞歸】:ide

  1. 分解 -- 將當前區間一分爲二,即求分裂點 mid = (low + high)/2; post

  2. 求解 -- 遞歸地對兩個子區間a[low...mid] 和 a[mid+1...high]進行歸併排序。遞歸的終結條件是子區間長度爲1。性能

  3. 合併 -- 將已排序的兩個子區間a[low...mid]和 a[mid+1...high]歸併爲一個有序的區間a[low...high]。

    學習

3. 歸併步驟:

  1. 申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列;

  2. 設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置;

  3. 比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置;

  4. 重複步驟 3 直到某一指針達到序列尾;

  5. 將另外一序列剩下的全部元素直接複製到合併序列尾。

  

 

3. 動畫演示

  

  這裏寫圖片描述 

 

4. 性能分析

1. 時間複雜度

  歸併排序的效率是比較高的,設數列長爲N,將數列分開成小數列一共要 logN 步,每步都是一個合併有序數列的過程,時間複雜度能夠記爲O(N),故一共爲O(N*logN),故時間複雜度爲O(nlogn)。

2. 空間複雜度

  歸併排序過程當中,須要一個輔助空間來暫存兩有序子文件歸併的結果,所以空間複雜度爲O(n)。

3. 算法穩定性 

  歸併是穩定的算法,在分解和並歸過程當中元素相對順序未發生改變。

4. 初始順序狀態

  1. 比較次數:
  2. 移動次數:
  3. 複雜度:    
  4. 排序趟數:

5. 歸位

  不能歸位

6. 優勢

  1. 排序:速度僅次於快速排序,爲穩定排序算法,通常用於對整體無序,可是各子項相對有序的數列
  2. 求逆序對數:在歸併的過程當中計算每一個小區間的逆序對數,進而計算出大區間的逆序對數(也能夠用樹狀數組來求解)

7. 具體代碼

public class MergeSort {

    // 歸併排序(從上往下,遞歸)
    public static void mergeSortUp2Down(int [] arr, int start, int end){
        if(arr!=null && start<end){//當子序列中只有一個元素時結束遞歸
            int mid=(start+end)/2;//劃分子序列
            mergeSortUp2Down(arr, start, mid);//對左側子序列進行遞歸排序
            mergeSortUp2Down(arr, mid+1, end);//對右側子序列進行遞歸排序
            merge(arr, start, mid, end);//合併
        }
    }
    
    //歸併排序(從下往上,迭代)
    public static void mergeSortDown2Up(int [] arr){
        if(arr!=null){
            int len = arr.length;
            //gap表示有序數組的長度(1,2,4,8……)
            for(int gap = 1; gap < len; gap*=2){
                int i;
                // 將"每2個相鄰的子數組" 進行合併排序。
                for(i = 0; i+2*gap-1 < len; i+=(2*gap)){
                    merge(arr, i, i+gap-1, i+2*gap-1);
                }
                // 若 i+gap-1 < len-1,則剩餘一個子數組沒有配對。
                // 將該子數組合併到已排序的數組中。
                if (i+gap-1 < len-1){
                    merge(arr, i, i+gap-1, len-1);
                }
                
            }
        }
    }
    
    //兩路歸併算法,兩個排好序的子序列合併爲一個子序列
    public static void merge(int []arr, int left, int mid, int right){
        int []tmp=new int[arr.length];//輔助數組
        int p1=left,p2=mid+1,k=left;//p一、p2是檢測指針,k是存放指針

        while(p1<=mid && p2<=right){
            if(arr[p1]<=arr[p2])
                tmp[k++]=arr[p1++];
            else
                tmp[k++]=arr[p2++];
        }

        while(p1<=mid) tmp[k++]=arr[p1++];//若是第一個序列未檢測完,直接將後面全部元素加到合併的序列中
        while(p2<=right) tmp[k++]=arr[p2++];//同上

        //複製回原素組
        for (int i = left; i <=right; i++) 
            arr[i]=tmp[i];
    }
    
    public static void main(String[] args){
        int[] arr = { 49, 38, 65, 97, 76, 13, 27, 50 };
        //mergeSortUp2Down(arr, 0, a.length-1);
        mergeSortDown2Up(arr);
        System.out.println("排好序的數組:");
        for (int e : arr)
            System.out.print(e+" ");
    }
}

8. 參考網址

  1. 數據結構基礎學習筆記目錄
  2. 排序算法系列之並歸排序
  3. 圖解排序算法(四)之歸併排序
  4. https://visualgo.net/en/sorting
  5. https://www.runoob.com/w3cnote/merge-sort.html
  6. https://github.com/hustcc/JS-Sorting-Algorithm
  7. 歸併排序
  8. Java實現歸併排序-有圖有真相
相關文章
相關標籤/搜索