歸併排序:歸併排序是建立在歸併操作上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併。
數據結構的學習過程中,我們經常會遇到排序算法,其中歸併排序是一種高效並且算法複雜度比較簡單的一種。在課本的介紹中,大部分都會介紹歸併排序算法。但是,每次看書總是覺得很容易,自己嘗試去實現時,總是會出錯。學習數據結構已經有一段時間了,但是直接讓我裸寫歸併排序的代碼,也需要花上不少時間去調試。難道就沒有更好的方式讓我們記住代碼嗎?
其實只要我們在寫代碼時,注意下技巧,便可輕鬆實現歸併排序算法。下面介紹下我使用的方式:
第一步:先寫一個合併兩個排序好數組的方法,方法名就叫做merge,如下:
public static void merge(int[] a, int aSize, int[] b, int bSize, int[] c){
int tempA = 0, tempB = 0, tempC = 0;
while(tempA < aSize && tempB < bSize){
if(a[tempA] > b[tempB]){
c[tempC++] = b[tempB++];
}else{
c[tempC++] = a[tempA++];
}
}
while(tempA < aSize){
c[tempC++] = a[tempA++];
}
while(tempB < bSize){
c[tempC++] = b[tempB++];
}
}
這個方法非常簡單,一共有着5個參數(也可以簡化爲3個參數),其中a,b數組是待合併數組,aSize,bSize是數組長度(這兩個參數可以去掉),c爲目標數組。主要的流程就是不斷的比較a,b數組的大小,然後將較小數據複製進c中。這裏面關鍵的一點就是使用了3個臨時變量,用於標誌每個數組對應的位置,這樣子可以極大簡化我們的代碼設計。下面是對應的圖示過程:
有了這個方法之後,我們就可以開始寫歸併排序的主體方法了。寫主體方法也很簡單,思想就是分治算法。
- 第一步:就是將大數組分成兩個小的數組
- 第二部:排序這兩個數組,使用的是遞歸排序方法,也就是自己調用自己
- 第三部:調用上面的合併方法合併起來即可
public class TowersApp{
public static void main(String[] args){
int[] a = {1,1,0,1,1,5,3};
mergeSort(a);
for(int i=0; i<a.length; i++){
System.out.print(a[i]);
}
}
public static void mergeSort(int[] source){
//遞歸出口
if(source.length == 1) return;
//將大數組分成兩個小數組
int middle = source.length / 2;
int[] left = new int[middle];
for(int i=0; i<middle; i++){
left[i] = source[i];
}
int[] right = new int[source.length - middle];
for(int i=middle; i<source.length; i++){
right[i-middle] = source[i];
}
//對數據進行排序(這裏使用遞歸排序)
mergeSort(left);
mergeSort(right);
//合併排序好的數據
merge(left, left.length, right, right.length, source);
}
public static void merge(int[] a, int aSize, int[] b, int bSize, int[] c){
int tempA = 0, tempB = 0, tempC = 0;
while(tempA < aSize && tempB < bSize){
if(a[tempA] > b[tempB]){
c[tempC++] = b[tempB++];
}else{
c[tempC++] = a[tempA++];
}
}
while(tempA < aSize){
c[tempC++] = a[tempA++];
}
while(tempB < bSize){
c[tempC++] = b[tempB++];
}
}
}
總結:要記住歸併排序算法的核心核心思想:分而治之。