歸併排序做爲最經典的分治算法之一,本質是利用遞歸把問題分解至最小子問題(即將原數組分解爲只有單個元素的子數組),而後遞歸開始「回升」,每一層回升都是在合併兩個有序數組(依次從兩個數組的頭部取出較小的元素放入目標數組,某一組所有取出後便可直接依次插入另外一數組的全部剩餘元素,兩組元素所有取完後排序結束),C語言代碼以下:算法
void merge(int arrA[], int lenA, int arrB[], int lenB, int arrTarget[]) { int iA = 0, iB = 0, iT = 0; for (; iA < lenA && iB < lenB; iT ++) { // 當兩個數組都還有剩餘元素時,比較每個元素並插入 if (arrA[iA] <= arrB[iB]) { arrTarget[iT] = arr[iA]; iA ++; } else { arrTarget[iT] = arr[iB]; iB ++; } } for (; iA < lenA; iA ++, iT ++) arrTarget[iT] = arrA[iA]; // 若數組A還有剩餘元素 for (; iB < lenB; iB ++, iT ++) arrTarget[iT] = arrA[iB]; // 若數組B還有剩餘元素 }
完成了這個merge,其實就已經作完了歸併排序的大部分工做,接下來咱們只要把前期準備作好就能夠了!也就是merge接受的兩個輸入數組必須是有序的,可是怎麼才能從亂序的原始數組中獲得兩個有序的子數組呢?
這裏咱們可使用二分法,也就是將原始數組從中點開始不斷往下分割,當每個子數組都只包含一個元素時,它也就達到了有序狀態。而這裏,天然也就是遞歸開始「回升」的節點,merge也能夠出現了。
因而整個排序的過程就是:不斷二分 -> 觸底(徹底分割) -> 回升數組
void mergeSort(int arr[], int fst, int lst, int arrTarget[]) { if (fst < lst) { // 二分(此處的函數將會在子數組的fst不大於lst時開始逐層返回,進行合併) int mid = (fst + lst) / 2; mergeSort(arr, fst, mid, arrTarget); mergeSort(arr, mid + 1, lst, arrTarget); // 合併 merge(arr + fst, mid - fst + 1, arr + mid + 1, lst - mid, arrTarget + fst); } }
運行結束後,arrTarget就是原數組的有序版本,它能夠在main函數中直接定義,不過也能夠經過進一步打包函數來避免這種麻煩並減小須要填寫的參數:函數
int* MergeSort(int arr[], int len) { int* arrTarget = (int*)malloc(sizeof(int) * len); // 此處不建議直接建立數組,函數結束後全部臨時變量的內存都將被釋放,任何操做均可能改變這裏的數據 if (arrTarget == NULL) { // 內存申請失敗 printf("Error: malloc failed.\n"); exit(1); } mergeSort(arr, 0, len - 1, arrTarget); return arrTarget; }
如今只須要填上原始數組和長度就能夠獲得一個排好序的新數組的指針啦!指針