歸併排序
1、原理
歸併排序是一種概念上最簡單的排序算法,與快速排序一樣,歸併排序也是基於分治法的。歸併排序將待排序的元素序列分成兩個長度相等的子序列,爲每一個子序列排序,然後再將他們合併成一個子序列。合併兩個子序列的過程也就是兩路歸併。
2、複雜度
歸併排序是一種穩定的排序算法,歸併排序的主要問題在於它需要一個與待排序數組一樣大的輔助數組空間。由於歸併排序每次劃分時兩個子序列的長度基本一樣,所以歸併排序最好、最差和平均時間複雜度都是nlog2n。
我們可以通過下圖非常容易看懂歸併排序的過程:
要將兩個排好序的子序列合併爲一個子序列的方法:每次都是從未比較的兩個子序列的最小值中選出一個更小值。
複雜度:
3、完整Java代碼
import org.junit.Test;
public class MergeSort {
//兩路歸併算法,兩個排好序的子序列合併爲一個子序列
public void merge(int []a,int left,int mid,int right){
int []tmp=new int[a.length];//輔助數組
int p1=left,p2=mid+1,k=left;//p1、p2是檢測指針,k是存放指針
while(p1<=mid && p2<=right){
if(a[p1]<=a[p2])
tmp[k++]=a[p1++];
else
tmp[k++]=a[p2++];
}
while(p1<=mid) tmp[k++]=a[p1++];//如果第一個序列未檢測完,直接將後面所有元素加到合併的序列中
while(p2<=right) tmp[k++]=a[p2++];//同上
//複製回原素組
for (int i = left; i <=right; i++)
a[i]=tmp[i];
}
public void mergeSort(int [] a,int start,int end){
if(start<end){//當子序列中只有一個元素時結束遞歸
int mid=(start+end)/2;//劃分子序列
mergeSort(a, start, mid);//對左側子序列進行遞歸排序
mergeSort(a, mid+1, end);//對右側子序列進行遞歸排序
merge(a, start, mid, end);//合併
}
}
@Test
public void test(){
int[] a = { 49, 38, 65, 97, 76, 13, 27, 50 };
mergeSort(a, 0, a.length-1);
System.out.println("排好序的數組:");
for (int e : a)
System.out.print(e+" ");
}
}
4、歸併與快排的比較
在同一臺計算機上得到兩個算法在不同數組長度下的執行時間:
快排和歸併的理論上的時間複雜性如下表:
(1)在數組長度小於一千萬的時候,如下圖,快速排序的速度要略微快于歸並排序,可能是因爲歸併需要額外的數組開銷(比如聲明臨時local數組用來儲存排序結果),這些操作讓歸併算法在小規模數據的並不佔優勢。
(2)但是,當數據量達到億級時,歸併的速度開始超過快速排序了,如下圖,因爲歸併排序比快排要穩定,所以在數據量大的時候,快排容易達到O(n^2)的時間複雜度,當然這裏是指未改進的快排算法。