先介紹下分治法,分治法就是將原問題分解爲幾個規模較小但在形式上跟原問題同樣的子問題,遞歸地求解這些子問題,而後再合併這些子問題的解,來創建原問題的解。歸併排序就是典型的遵循分治法思想的算法,歸併排序的每一步,都將原問題分解爲左右兩部分,處理完這兩部分後,將此兩部分合並,再往上遞歸,最後就是整個問題的解了。css
考慮如下序列:算法
4緩存 |
6函數 |
1spa |
3指針 |
9blog |
5排序 |
8遞歸 |
2內存 |
此時,將此問題分解爲兩半
繼續分解,直接每部分只有兩個元素,例如左邊分解爲如下兩部分
這個時候,直接對這兩部分分別排序,由於這兩部分恰好是排好序的,因此獲得
這個時候,須要對這兩部分的處理結果進行合併處理,合併的時候須要一個額外的空間(設爲A)來存儲此合併結果,合併完後,再將此空間內的數據,覆蓋到原空間中去。合併處理流程以下:同時對左右兩部分中的元素進行遍歷,以下圖所示:
此時比較兩部分所指向的元素,1比較小,則將1拷貝到A中,而後將右邊的指針指向下一個元素,依次類推,將3和4比較,3比較小,則將3拷貝到A中後, 右邊的拷貝完了,將再也不從中取元素,而從左邊依次將元素拷貝到A中,最後A的結果以下:
1 |
3 |
4 |
6 |
一樣的,右邊的進行處理後,將獲得
2 |
5 |
8 |
9 |
而後,再次對
進行以上合併操做,就是最後的解了
void MergeSort(int numArr[], int size){
int* pBuff = new int[size];
_MergeSort(numArr, pBuff, 0, size - 1);
delete[] pBuff;
}
void _MergeSort(int numArr[], int pBuff[], int left, int right){
if (right > left){
int mid = (left + right) / 2;
_MergeSort(numArr, pBuff, left, mid);
_MergeSort(numArr, pBuff, mid + 1, right);
//將numArr[left,mid]和numArr[mid + 1,right]合併
_Merge(numArr, pBuff, left, mid + 1, right);
}
}
void _Merge(int numArr[], int pBuff[], int lPos, int rPos, int rEnd){
int nLEnd = rPos - 1;
int i = lPos;
int j = rPos;
int n = 0;
int nSize = rEnd - lPos + 1;
while (true){
if (i <= nLEnd && j <= rEnd)
pBuff[n++] = (numArr[i] < numArr[j]) ? numArr[i++] : numArr[j++];
else if (i <= nLEnd)
pBuff[n++] = numArr[i++];
else if (j <= rEnd)
pBuff[n++] = numArr[j++];
else
break;
}
memcpy(numArr + lPos, pBuff, nSize * sizeof(int));
}
以上代碼最關鍵的部分就是_Merge函數,要作到合併的時候,各部分不越界,要很是細緻的檢查合併的左右部分的超始和終止位置,還有,不要在_Merge函數中進行申請緩存區的操做,由於_Merge函數的大量調用,進行malloc操做會大大下降算法速度,只在最外層調用一次申請內存操做就能夠了。
歸併排序的時間複雜度爲O(NlogN)