【資料結構和演算法14】歸併排序
歸併演算法的中心是歸併兩個已經有序的陣列。歸併兩個有序陣列A和B,就生成了第三個陣列C,陣列C包含陣列A和B的所有資料項,並且使它們有序的排列在陣列C中。首先我們來看看歸併的過程,然後看它是如何在排序中使用的。
假設有兩個有序陣列,不要求有相同的大小。設陣列A有4個數據項,陣列B有6個數據項,它們要被歸併到陣列C中,開始時陣列C有10個儲存空間,歸併過程如下圖所示:
歸併排序的思想是把一個數組分成兩半,排序每一半。然後用merge方法將陣列的兩半歸併成一個有序的陣列。被分的每一半使用遞迴,再次劃分排序,直到得到的子陣列只含有一個數據項為止。正如上面所說的,歸併排序需要額外的一個和AB兩個陣列總和相等的空間,如果初始陣列幾乎沾滿了整個儲存器,那麼歸併排序就不能工作了。
歸併排序的思想很簡單,下面我們來看看具體實現:
public void mergeSort(int[] source) { int[] workSpace = new int[source.length]; recMergeSort(source,workSpace, 0, source.length-1); } private void recMergeSort(int[] source, int[] workSpace, int lowerBound, int upperBound) { if(lowerBound == upperBound) { return; } else { int mid = (lowerBound + upperBound) / 2; recMergeSort(source, workSpace, lowerBound, mid); //左邊排 recMergeSort(source, workSpace, mid+1, upperBound); //右邊排 merge(source, workSpace, lowerBound, mid+1, upperBound);//歸併 } } private void merge(int[] a, int[] workSpace, int lowPtr, int highPtr, int upperBound) { int j = 0; int lowerBound = lowPtr; int mid = highPtr - 1; int n = upperBound - lowerBound + 1; while(lowPtr <= mid && highPtr <= upperBound) { if(a[lowPtr] < a[highPtr]) { workSpace[j++] = a[lowPtr++]; } else { workSpace[j++] = a[highPtr++]; } } while(lowPtr <= mid) { workSpace[j++] = a[lowPtr++]; } while(highPtr <= upperBound) { workSpace[j++] = a[highPtr++]; } for(j = 0; j < n; j++) { a[lowerBound + j] = workSpace[j]; } }
演算法分析:歸併排序的執行時間最差、最好和平均都是O(NlogN),但是它需要額外的儲存空間,這在某些記憶體緊張的機器上會受到限制。歸併演算法是由分割和歸併兩部分組成的,對於分各部分,如果我們使用二分查詢,時間是O(NlogN),在最後歸併的時候時間是O(N),所以總時間是O(NlogN)。空間複雜度為O(N)。
歸併排序是穩定的,由於沒有發生資料交換,所有當a=b的時候,a一開始如果在b前面,則其每一次合併後仍然在b前面,故該排序演算法是穩定的。
歸併排序就寫這麼多,如有錯誤之處,歡迎留言指正~
文末福利:“程式設計師私房菜”,一個有溫度的公眾號~
_____________________________________________________________________________________________________________________________________________________
-----樂於分享,共同進步!