歸并排序的基本思想是:利用遞歸和分而治之(Divide and Conquer)的方法將待排序的數組劃分成越來越小的局部數組,再對局部數組排序,最后利用遞歸的方法將已經排序完畢的局部數組整合成越來越大的有序數組。歸并排序包括兩個步驟:
一、分割(Divide);
二、整合(Conquer):
首先來看一下歸并排序中用到的下標:left 指局部數組開頭的元素,right 指局部數組末尾+1 的元素,mid 是 left 和 right 相加除以2,對結果向下取整。
歸并排序中用到的下標.png
接下來我們通過對數組 {9, 6, 7, 2, 5, 1, 8, 4, 2} 對歸并排序進行說明:
歸并排序.jpg
一、分割
向下的藍色箭頭代表分割,箭頭邊的數字表示處理順序,分割由mergeSort負責,當局部數組只剩下一個元素時,mergeSort不做任何處理直接結束,如果不是,則計算數組的中央位置 mid, 將 left 到 mid (不包含mid)視作前半部分,mid 到 right (不包含right)視作后半部分,再分別套用mergeSort;具體步驟如下所示:
Step 1: left=0, right=9, 因此 mid=4, 調用mergeSort進行分割,將0~4(即9、6、7、2)視作前半部分,;
Step 2: left=0, right=4, 因此 mid=2, 調用mergeSort進行分割,將0~2(即9、6)視作前半部分;
Step 3: left=0, right=2, 因此 mid=1, 調用mergeSort進行分割,將0~1(即9)視作前半部分,此時局部數組只剩一個元素,mergeSort不做任何處理直接結束;
Step 4: left=1, right=2, 將1~2(即6)視作后半部分,此時局部數組只剩一個元素,mergeSort不做任何處理這節結束;
Step 5: 接下來對對 {6} 和 {9} 這這兩個局部數組進行合并,因此就有了第二個步驟。
二、整合
向上的橘色箭頭代表整合,箭頭邊的數字表示處理順序,整合由merge負責。為了方便敘述,在這里將包含 n1 個元素的前半部分已排序數組稱為 L,包含 n2 個元素的后半部分有序數組稱為 R, 現在我們需要將 L 和 R 中的元素按照升序排列復制到數組 A 中,在這里我們可以利用已排序的性質進行合并,同樣我們舉個例子進行說明。
合并兩個已排序的數組.jpg
Step 5: 調用merge對前半部分數組和后半部分數組進行合并,結果為6、9;
Step 6: left=0, right=4, 因此 mid=2, 調用mergeSort進行分割,將2~4(即7、2)視作后部分;
step 7: left=2, right=4, 因此 mid=3 調用mergeSort進行分割,將2~3(即7)視作前半部分,此時局部數組只剩一個元素,mergeSort不做任何處理這節結束;
Step 8: left=3, right=4, 將3~4(即2)視作后半部分,此時局部數組只剩一個元素,mergeSort不做任何處理這節結束;
Step 9: 接下來調用merge對 {7} 和 {2} 這兩個局部數組進行合并,結果為 {2、7};
Step 10: 調用merge對 {6, 9} {2, 7}這兩個局部數組進行合并,結果為 {2, 6, 7, 9};
Step ........: 以此類推。
Step 24: 最終得到排序的結果為 {1, 2, 2, 4, 5, 6, 7, 8, 9}。
接下來貼上代碼:
<pre>
void merge(int A[], int n, int left, int mid, int right){
int n1 = mid - left;
int n2 = right - mid;
for (int i = 0; i < n1; i++) L[i] = A[left+i];
for (int i = 0; i < n2; i++) R[i] = A[mid + i];
L[n1] = R[n2] = SENTINEL;
int i = 0, j = 0;
for (int k = left; k < right; k++){
cnt++;
if (L[i] <= R[j]){
A[k] = L[i++];
}
else{
A[k] = R[j++];
}
}
}
//歸并排序
void mergeSort(int A[], int n, int left, int right){
if (left + 1 < right){
int mid = (left + right) / 2;
mergeSort(A, n, left, mid);
mergeSort(A, n, mid, right);
merge(A, n, left, mid, right);
trace(A, n);
}
}
</pre>
運行結果:
運行結果.png
穩定性:歸并排序包含不相鄰元素間的比較,但并不會直接交換,在合并兩個已排序數組的時候,如果遇到了相同元素,只要保證前半部分數組優先于后半部分數組,相同元素的順序就不會顛倒,因此歸并排序屬于穩定的排序算法。
復雜度:在merge處理中,合并算法的復雜度為O(n1+n2),對于本例的輸入{9, 6, 7, 2, 5, 1, 8, 4}包含9個元素,若想將其分割成僅包含一個元素的局部數組,需要經歷9-5-3-2-1的4次分割,總共分為5層,如果是8個元素,則分為4層。一般來說n個數據大致分為log2(n)層。由于每層執行merge的復雜度為O(n), 因此歸并排序的整體復雜度為O(nlogn)。
總結:歸并排序算法雖然高效穩定,但是在處理過程中,除了用于保存輸入數據的數組之外,還需要臨時占用一部分內存空間。