聲明
本文是博主在Coursera學習時所寫的學習筆記,如有錯誤疏漏還望各位指正。
歡迎交流討論
如果大家轉載,請註明本文地址!
歸併排序
基本思想
- 將列表分爲兩部分
- 通過遞歸將每部分排序
- 合併兩部分
舉個例子
輸入一個數組
將其分爲兩部分,並對每個部分排序,如下圖
合併兩部分
新建一個aux[]的拷貝a[]
設兩部分最小元素的下標爲i,j,每次比較i,j所對應的元素大小,將較小的元素放入a[],讓後將i或j加1,直至將兩部分合並完成。
算法分析
- 時間複雜度
比較次數:O(NlogN)
數組訪問次數:O(6NlogN) - 空間複雜度
需要額外的O(N) ,這是歸併排序最明顯的缺點
代碼實現
public class MergeSort {
//合併
private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
for (int k = lo; k <= hi; k++)
aux[k] = a[k];
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++) {
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (less(aux[j], aux[i]))
a[k] = aux[j++];
else
a[k] = aux[i++];
}
}
private static boolean less(Comparable a, Comparable b) {
return a.compareTo(b) < 0;
}
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
if (hi <= lo)
return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid + 1, hi);
merge(a, aux, lo, mid, hi);
}
public static void sort(Comparable[] a) {
Comparable[] aux = new Comparable[a.length];
sort(a, aux, 0, a.length - 1);
}
}
改進
對劃分後較小的數組使用插入排序
因爲遞歸的原因,歸併排序對較小的數組有很大開銷,因此對較小的數組我們改用插入排序
將上述的sort(Comparable[] a, Comparable[] aux, int lo, int hi)改爲如下:
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
//當數組長度小於CUTOFF時使用插入排序,CUTOFF應設置爲常數。
if (hi <= lo + CUTOFF - 1)
{
Insertion.sort(a, lo, hi);
return;
}
int mid = lo + (hi - lo) / 2;
sort (a, aux, lo, mid);
sort (a, aux, mid+1, hi);
merge(a, aux, lo, mid, hi);
}
如果歸併前排序已經完成則停止
對劃分後的兩部分分別排序後,如果左邊最大的元素小於右邊最大的元素,則不要進行歸併
將sort(Comparable[] a, Comparable[] aux, int lo, int hi)改爲如下:
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort (a, aux, lo, mid);
sort (a, aux, mid+1, hi);
//新添加的代碼
if (!less(a[mid+1], a[mid])) return;
merge(a, aux, lo, mid, hi);
}
自下而上歸併排序
基本思想
將數組劃分爲長度爲1的若干數組,合併成爲長度爲2的若干數組。
將長度爲2的書組合併成長度爲4的若干數組。以此類推,直至排序完成。
舉個例子
示例圖
代碼實現
將sort(Comparable[] a)改爲如下:
public static void sort(Comparable[] a)
{
int N = a.length;
Comparable[] aux = new Comparable[N];
for (int sz = 1; sz < N; sz = sz+sz)//每次歸併的數組長度,1,2,4,8....
for (int lo = 0; lo < N-sz; lo += sz+sz)//每次歸併的子數組a[lo]~a[N-sz]
merge(a, aux, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));
}
說明
這種自下而上的歸併排序是一種簡單的無遞歸歸併排序,但是要大約比應用遞歸的自頂而下的歸併排序慢10%