歸併排序,整體就是一個簡單的遞歸過程,左邊排好序,右邊排好序,讓其整體有序。讓其整體有序的過程中用到了外排序的方法。
坦白的說,這種排序不好去理解,歸併的代碼我是看了好久才學會的,自己也練了好多遍,感覺還是暈暈的,這種排序要靠自己去領悟的,我是用最笨的方法,把所有執行的過程全部寫出來,結果自己看懂了!順便也推出的它的時間複雜度,實在不好用語言說清楚的;下面我就先寫出代碼;然後畫圖推出它的時間複雜度;
public class Code_MergeSort {
private static final int[] arr = { 20, 17, 18, 11, 10 };
public static void main(String[] args) {
mergeSort(arr, 0, arr.length - 1);
}
/**
* @param arr
* 數組
* @param left
* 數組的左邊開始下標
* @param right
* 數組的右邊開始下標
*/
private static void mergeSort(int[] arr, int left, int right) {
if (left == right)
return;
int middle = left + ((right - left) >> 1);// 中間的下標
mergeSort(arr, left, middle);// 左邊遞歸
mergeSort(arr, middle + 1, right);// 右邊遞歸
merge(arr, left, middle, right);// 兩邊都調用完了遞歸以後使用外排序的方法
}
private static void merge(int[] arr, int left, int middle, int right) {
int[] help = new int[right - left + 1];// 輔助數組,大小相同起始結束位置相同
int i = 0;// 輔助數組的起始下標
int p1 = left;// 左邊數組的起始的下標
int p2 = middle + 1;// 右邊數組的起始的下標
while ((p1 <= middle) // 左邊數組不越界
&& (p2 <= right)) // 右邊數組不越界
{
help[i++] = (arr[p1] < arr[p2]) ? arr[p1++] : arr[p2++];
}
//執行完上邊以後一個越界,一個不越界,把不越界的數組全部copy到輔助數組之中
while (p1 <= middle) {
help[i++] = arr[p1++];
}
while (p2 <= right) {
help[i++] = arr[p2++];
}
for (int j = 0; j < help.length; j++) {
arr[left + j] = help[j];
}
}
}
以上就是全部的代碼:
下面列出上面代碼執行的所有過程;
比如下面這個數組:
數組下標index | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
數組數值value | 20 | 17 | 18 | 11 | 10 |
按照上面的代碼簡單的執行過程如下面的圖所示:
畫紅色的方框代表結束遞歸結束,這樣的次數和數組的大小相同;
其他的都是遞歸自身的外排序方法;
現在我們來估算它的時間複雜度:
假設數組的長度是N個;上面的結構是一個二叉樹的結構,他實際上的高度是多少(不包含像紅色方框的數據)
,根據公式總共有:log2N層的高度,那麼每一層的執行的次數的多少呢?也就是外排序方法執行的次數是多少;
第一層 N長度數組------要執行N次;
第二層 N/2長度數組-----要執行(N/2)次;
第三層 N/4長度數組-----要執行(N/4)次;
第四層 N/8長度數組-----要執行(N/8)次;
第五層 N/16長度數組-----要執行(N/16)次;
…
上面就是最差情況;
所以時間複雜度的推導公式 f(N) = N+(N/2)+…;總共有log2N個,
所以時間複雜度是
(x(1-(1/2)x))/(1-(1/2)),
把x=log2N帶入上面的公式 得出時間複雜度是Nlog2N
所以歸併排序的時間複雜度是O(n) = nlog2n;