看動畫學算法之:排序-歸併排序

簡介

歸併排序簡稱Merge sort是一種遞歸思想的排序算法。這個算法的思路就是將要排序的數組分紅不少小的部分,直到這些小的部分都是已排序的數組爲止(只有一個元素的數組)。java

而後將這些排序過的數組兩兩合併起來,組成一個更大一點的數組。接着將這些大一點的合併過的數組再繼續合併,直到排序完整個數組爲止。git

歸併排序的例子

假如咱們有一個數組:29,10,14,37,20,25,44,15,怎麼對它進行歸併排序呢?github

先看一個動畫:算法

咱們來詳細分析一下上面例子的運行過程:數組

首先將數組分爲兩部分,[29,10,14,37]和[20,25,44,15]。ide

[29,10,14,37]又分紅兩部分[29,10]和[14,37]。動畫

[29,10]又被分紅兩部分[29]和[10],而後對[29]和[10]進行歸併排序生成[10,29]。spa

一樣的對[14,37]進行歸併排序獲得[14,37]。3d

將[10,29]和[14,37]再次進行歸併排序獲得[10,14,29,37],以此類推,獲得最後的結果。code

歸併排序算法思想

歸併排序主要使用了分而治之的思想。將一個大的數組分紅不少不少個已經排序好的小數組,而後再對小數組進行合併。

這個Divide的過程可使用遞歸算法,由於無論是大數組仍是小數組他們的divide邏輯是同樣的。

而咱們真正作排序的邏輯部分是在合併這一塊。

歸併排序的java實現

先看一下最核心的merge部分:

/** *合併兩部分已排序好的數組 * @param array 待合併的數組 * @param low 數組第一部分的起點 * @param mid 數組第一部分的終點,也是第二部分的起點-1 * @param high 數組第二部分的終點 */
    private void merge(int[] array, int low, int mid, int high) {
        // 要排序的數組長度
        int length = high-low+1;
        // 咱們須要一個額外的數組存儲排序事後的結果
        int[] temp= new int[length];
        //分紅左右兩個數組
        int left = low, right = mid+1, tempIdx = 0;
        //合併數組
        while (left <= mid && right <= high) {
            temp[tempIdx++] = (array[left] <= array[right]) ? array[left++] : array[right++];
        }
        //一個數組合並完了,剩下的一個繼續合併
        while (left <= mid) temp[tempIdx++] = array[left++];
        while (right <= high) temp[tempIdx++] = array[right++];
        //將排序事後的數組拷貝回原數組
        for (int k = 0; k < length; k++) array[low+k] = temp[k];
    }
複製代碼

你們須要注意的是,咱們的元素是存在原始數組裏面的,方法的第一個參數就是原始數組。

後面的三個參數是數組中須要歸併排序的index。三個index將數組劃分紅了兩部分:array[low to mid], array[mid+1 to high]。

merge的邏輯就是對這兩個數組進行合併。

由於咱們的數組自己是存放有原始的,因此要想進行歸併排序,咱們須要藉助一個額外的數組空間int[] temp。

經過比較array[low to mid], array[mid+1 to high]中的元素大小,一個個將元素插入到int[] temp中,最後將排序事後的數組拷貝回原數組,merge完成。

而後咱們再看一下divide的部分,divide部分實際上就是遞歸調用,在遞歸的最後,咱們須要調用merge方法便可:

public void doMergeSort(int[] array, int low, int high){
        // 要排序的數組 array[low..high]
        //使用二分法進行遞歸,當low的值大於或者等於high的值的時候,就中止遞歸
        if (low < high) {
            //獲取中間值的index
            int mid = (low+high) / 2;
            //遞歸前面一半
            doMergeSort(array, low  , mid );
            //遞歸後面一半
            doMergeSort(array, mid+1, high);
            //遞歸完畢,將排序事後的數組的兩部分合並
            merge(array, low, mid, high);
            log.info("merge以後的數組:{}",array);
        }
    }
複製代碼

array是原數組,low和high標記出了要遞歸排序的數組起始位置。

運行下上面的結果:

能夠看到輸出結果和咱們動畫展現的結果是一致的。

歸併排序的時間複雜度

咱們看下歸併排序的時間複雜度是怎麼樣的。

首先看merge方法,merge方法實際是遍歷了兩個數組,因此merge方法的時間複雜度是O(N)。

再看一下divide方法:

divide方法將排序分紅了logN層,每層均可以看作是對N個元素的合併排序,所以每層的時間複雜度是O(N)。

加起來,總的時間複雜度就是O(N logN)。

本文的代碼地址:

learn-algorithm

本文已收錄於 www.flydean.com/algorithm-m…

最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!

相關文章
相關標籤/搜索