看動畫學演算法之:排序-歸併排序
目錄
簡介
歸併排序簡稱Merge sort是一種遞迴思想的排序演算法。這個演算法的思路就是將要排序的陣列分成很多小的部分,直到這些小的部分都是已排序的陣列為止(只有一個元素的陣列)。
然後將這些排序過的陣列兩兩合併起來,組成一個更大一點的陣列。接著將這些大一點的合併過的陣列再繼續合併,直到排序完整個陣列為止。
歸併排序的例子
假如我們有一個數組:29,10,14,37,20,25,44,15,怎麼對它進行歸併排序呢?
先看一個動畫:
我們來詳細分析一下上面例子的執行過程:
首先將陣列分為兩部分,[29,10,14,37]和[20,25,44,15]。
[29,10,14,37]又分成兩部分[29,10]和[14,37]。
[29,10]又被分成兩部分[29]和[10],然後對[29]和[10]進行歸併排序生成[10,29]。
同樣的對[14,37]進行歸併排序得到[14,37]。
將[10,29]和[14,37]再次進行歸併排序得到[10,14,29,37],以此類推,得到最後的結果。
歸併排序演算法思想
歸併排序主要使用了分而治之的思想。將一個大的陣列分成很多很多個已經排序好的小陣列,然後再對小陣列進行合併。
這個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)。
本文的程式碼地址:
本文已收錄於 http://www.flydean.com/algorithm-merge-sort/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!