歸併,將兩個有序的數組歸併成一個更大的有序數組。算法
要將一個數組排序,先(遞歸地)將數組分紅兩半分別排序,而後將結果歸併起來。數組
歸併排序優勢:保證將任意長度爲的數組排序所須要時間與
成正比。bash
歸併排序缺點:所需的額外空間和成正比。less
歸併很簡單的作法是創建第三個數組,將兩個數組合併到第三個數組中。這種作法在咱們作大數組歸併排序的時候並不可取,由於會作屢次歸併,致使申請過多的額外空間。因此最好就是咱們可以在原有數組上進行歸併,這樣就不會有過多的存儲空間申請。ui
原地歸併的抽象方法:spa
/**
* 將input[start...mid]和input[mid+1...end]歸併
*
*/
private static void merge(Comparable[] input, int start, int mid, int end) {
int startIndex = start;
int endIndex = mid + 1;
for (int i = 0; i < end + 1; i++) {
tempInput[i] = input[i];
}
for (int i = start; i < end + 1; i++) {
if (startIndex > mid) {
input[i] = tempInput[endIndex++];
} else if (endIndex > end) {
input[i] = tempInput[startIndex++];
} else if (less(input[startIndex], input[endIndex])) {
input[i] = tempInput[startIndex++];
} else {
input[i] = tempInput[endIndex++];
}
}
}
複製代碼
自頂向下的歸併排序算法是典型的採用高效算法設計中分治思想的例子。遞歸將數組平分,直到比較的兩個數組都是一個,而後自底向上進行歸併。設計
自頂向下的歸併排序:3d
private static Comparable[] tempInput;
/**
* 歸併排序
*
*/
public static void mergeSort(Comparable[] input) {
tempInput = new Comparable[input.length];
mergeSort(input, 0, input.length);
}
/**
* 分治的思想,採用遞歸
*
*/
private static void mergeSort(Comparable[] input, int start, int end) {
if (start >= end) {
return;
}
int mid = (start + end) / 2;
mergeSort(input, start, mid);
mergeSort(input, mid + 1, end);
merge(input, start, mid, end);
}
複製代碼
例子:code
自底向上的歸併排序與自頂向下不一樣的是沒有遞歸分組,採用循環遍歷的方式分組。先是兩兩分組歸併(每一個元素想像成一個數組),接着四四分組歸併(每兩個元素組成一個數組),接下來是八八分組歸併(每四個元素組成一個數組),以此類推。在循環遍歷過程當中,可能會出現最後一次歸併的第二個數組的大小小於第一個數組,但這對merge()
方法不是問題。cdn
自底向上的歸併排序:
private static void mergeSortB(Comparable[] input) {
int length = input.length;
for (int i = 1; i < length; i += i) {
for (int j = 0; j < length; j += 2 * i) {
merge(input, j, j + i - 1, Math.min(j + i + i - 1, length - 1));
}
}
}
複製代碼
另外,歸併排序須要處理不少小型數組的排序,對於小型的數組的排序徹底能夠用插入排序或選擇排序等簡單的排序,極可能比歸併排序的速度更快。
參考自《算法》