標籤:
歸併排序採用的是遞迴來實現,屬於“分而治之”,將目標數組從中間一分為二,之後分別對這兩個數組進行排序,排序完畢之後再將排好序的兩個數組“歸併”到一起,歸併排序最重要的也就是這個“歸併”的過程,歸併的過程中需要額外的跟需要歸併的兩個數組長度一致的空間,比如需要規定的數組分別為: [3, 6, 8, 11] 和 [1, 3, 12, 15] (雖然邏輯上被劃為為兩個數組,但實際上這些元素還是位於原來數組中的,只是通過一些 index 將其劃分成兩個數組,原數組為 [3, 6, 8, 11, 1, 3, 12, 15 ,我們設定三個指標 lo, mid, high 分別為 0,3,7 就可以實現邏輯上的子數組劃分)那麼需要的額外數組的長度為 4 + 4 = 8 。
歸併的過程可以簡要地概括為如下:
1) 將兩個子數組中的元素複製到新數組 copiedArray 中,以前面提到的例子為例,則 copiedArray = [3, 6, 8, 11, 1, 3, 12, 15] ;
2) 設定兩個指標分別指向原子數組中對應的第一個元素,假定這兩個指標取名為 leftIdx 和 rightIdx ,則 leftIdx = 0 (對應 copiedArray 中的第一個元素 [3] ), rightIdx = 4 (對應 copiedArray 中的第五個元素 [1] );
3) 比較 leftIdx 和 rightIdx 指向的數組元素值,選取其中較小的一個並將其值賦給原數組中對應的位置 i ,賦值完畢後分別對參與賦值的這兩個索引做自增 1 操作,如果 leftIdx 或 rigthIdx 值已經達到對應數組的末尾,則餘下只需要將剩下數組的元素按順序 copy 到餘下的位置即可。
下面給個歸併的具體執行個體:
第一趟:輔助數組 [21 , 28, 39 | 35, 38] (數組被拆分為左右兩個子數組,以 | 分隔開)[21 , , , , ] (第一次 21 與 35 比較 , 左邊子數組勝出, leftIdx = 0 , i = 0 )第二趟:輔助數組 [21, 28 , 39 | 35, 38][21 , 28, , , ] (第二次 28 與 35 比較,左邊子數組勝出, leftIdx = 1 , i = 1 )第三趟: [21, 28, 39 | 35 , 38] [21 , 28 , 35, , ] (第三次 39 與 35 比較,右邊子數組勝出, rightIdx = 0 , i = 2 )第四趟: [21, 28, 39 | 35, 38 ] [21 , 28 , 35 , 38, ] (第四次 39 與 38 比較,右邊子數組勝出, rightIdx = 1 , i = 3 )第五趟: [21, 28, 39 | 35, 38] [21 , 28 , 35 , 38 , 39] (第五次時右邊子數組已複製完,無需比較 leftIdx = 2 , i = 4 )
以上便是一次歸併的過程,我們可以將整個需要排序的數組做有限次拆分(每次一分為二)直到分為長度為1 的小數組為止,長度為 1 時數組已經不用排序了。在這之後再逆序(由於採用遞迴)依次對這些數組進行歸併操作,直到最後一次歸併長度為 n / 2 的子數組,歸併完成之後數組排序也完成。
歸併排序需要的額外空間是所有排序中最多的,每次歸併需要與參與歸併的兩個數組長度之和相同個元素(為了提供輔助數組)。則可以推斷歸併排序的空間複雜度為 1 + 2 + 4 + … + n = n * ( n + 2) / 4 (忽略了 n 的奇偶性的判斷),時間複雜度比較難估,這裡小弟也忘記是多少了(囧)。
歸併排序是建立在歸併操作上的一種有效排序演算法,歸併是指將兩個已經排序的序列合并成一個序列的操作
實現代碼:
/** * 歸併排序<br/> * <ul> * <li>申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合并後的序列</li> * <li>設定兩個指標,最初位置分別為兩個已經排序序列的起始位置</li> * <li>比較兩個指標所指向的元素,選擇相對小的元素放入到合并空間,並移動指標到下一位置</li> * <li>重複步驟3直到某一指標達到序列尾</li> * <li>將另一序列剩下的所有元素直接複製到定序序列尾</li> * </ul> * * @param numbers */ public static void mergeSort(int[] numbers, int left, int right) { int t = 1;// 每組元素個數 int size = right - left + 1; while (t < size) { int s = t;// 本次迴圈每組元素個數 t = 2 * s; int i = left; while (i + (t - 1) < size) { merge(numbers, i, i + (s - 1), i + (t - 1)); i += t; } if (i + (s - 1) < right) merge(numbers, i, i + (s - 1), right); } } /** * 歸併演算法實現 * * @param data * @param p * @param q * @param r */ private static void merge(int[] data, int p, int q, int r) { int[] B = new int[data.length]; int s = p; int t = q + 1; int k = p; while (s <= q && t <= r) { if (data[s] <= data[t]) { B[k] = data[s]; s++; } else { B[k] = data[t]; t++; } k++; } if (s == q + 1) B[k++] = data[t++]; else B[k++] = data[s++]; for (int i = p; i <= r; i++) data[i] = B[i]; }
Java演算法-歸併排序