歸併排序
基本思想
歸併排序的核心是將兩個有序序列合併爲一個有序序列。因此將兩個有序序列合併成一個有序序列是歸併排序的基礎算法。歸併排序主要分爲遞歸歸併排序與迭代歸併排序:
遞歸歸併排序即將無序的序列S[s:t]分成兩個部分:S[s:m]和S[m+1:t],m=(s+t)/2。
迭代歸併排序即將初始長度爲n的數組看成n個長度爲1的有序子表,然後將相鄰的有序子表兩兩合併,得到[n/2]個長度爲2或1的有序子表(如果n爲奇數,則最後一個有序子表的長度爲1),再兩兩歸併得到[n/4]個有序子表,以此類推,直至得到長爲n的有序序列。
操作方法
合併方法:
設a[l:h]是一個線性表(序列),將a[l:h]分爲兩個子序列a[l:m],a[m+1:h],其中,a[l:m]和a[m+1:h]分別有序。那麼我們設置兩個指針:x指針指向a[l],y指針指向a[m+1]。
當他們都不指向末尾時,將*x與*y進行比較,將較小的數放到b[]數組(保存數組)裏,同時,將較小的那個子序列指針右移。
當x指向末尾時,把y指向的元素及其之後的元素全部放進b[]即可。
當y指向末尾時,把x指向的元素及其之後的元素全部放進b[]即可。
遞歸歸併:
遞歸歸併排序即將無序的序列S[s:t]分成兩個部分:S[s:m]和S[m+1:t],m=(s+t)/2。
迭代歸併:
迭代歸併排序即將初始長度爲n的數組看成n個長度爲1的有序子表,然後將相鄰的有序子表兩兩合併,得到[n/2]個長度爲2或1的有序子表(如果n爲奇數,則最後一個有序子表的長度爲1),再兩兩歸併得到[n/4]個有序子表,以此類推,直至得到長爲n的有序序列。
算法實現
private static void Merge(int a[], int b[], int l, int m, int h) {
int i, j, k;
i = l; // i 指的是第一個子序列a[1:m]
j = m + 1; // j 指的是第二個子序列a[m+1:h]
k = l; // k 指的是最後用來存放合併後的數組的數組
while (i <= m && j <= h) {
if (a[i] < a[j]) {
b[k] = a[i];
i++;
k++;
} else {
b[k] = a[j];
j++;
k++;
}
}
while (i <= m) {
b[k++] = a[i++];
}
while (j <= h) {
b[k++] = a[j++];
}
}
// 迭代歸併算法
private static void mergePass(int S[], int T[], int n, int len) {
int i = 0;
while (i + 2 * len < n) {
Merge(S, T, i, i + len - 1, i + 2 * len - 1);
i += 2 * len;
}
if (i + len <= n) // 一個歸併段長len,另一個對並段長度不足len
{
Merge(S, T, i, i + len - 1, n - 1);
}
}
// 迭代實現的歸併排序算法
public static void mergeSort(int R[], int T[], int n) {
int temp[] = R.clone();
int len = 1;
while (len < n) {
mergePass(temp, T, n, len);
temp = T.clone(); // 不加這句的話,temp的數組一直沒有發生變化
len *= 2;
}
}
效率分析
算法性能:
時間複雜度:
歸併排序的形式就是一棵二叉樹,它需要遍歷的次數就是二叉樹的深度,而根據完全二叉樹的可以得出它的時間複雜度是O(n*log2n)。
空間複雜度
由前面的算法說明可知,算法處理過程中,需要一個大小爲n的臨時存儲空間用以保存合併序列。
算法穩定性
在歸併排序中,相等的元素的順序不會改變,所以它是穩定的。