排序演算法之 歸併排序 及其時間複雜度和空間複雜度
阿新 • • 發佈:2018-12-31
在排序演算法中快速排序的效率是非常高的,但是還有種排序演算法的效率可以與之媲美,那就是歸併排序;歸併排序和快速排序有那麼點異曲同工之妙,快速排序:是先把陣列粗略的排序成兩個子陣列,然後遞迴再粗略分兩個子陣列,直到子數組裡面只有一個元素,那麼就自然排好序了,可以總結為先排序再遞迴;歸併排序:先什麼都不管,把陣列分為兩個子陣列,一直遞迴把陣列劃分為兩個子陣列,直到數組裡只有一個元素,這時候才開始排序,讓兩個陣列間排好序,依次按照遞迴的返回來把兩個陣列進行排好序,到最後就可以把整個陣列排好序;
演算法分析
歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表實現程式碼
分析下上面程式碼:其實上面的程式碼主要的是兩個函式,第一個是劃分陣列函式,第二個是對兩個有序數組合並的歸併函式;這裡要藉助一個臨時陣列,有的人在main函式中申請動態陣列,然後讓所有遞迴呼叫都使用該陣列;也有的人在歸併函式裡申請個臨時陣列;而我的方法是定義一個全域性的臨時陣列;其實我感覺這幾個方法都是大同小異,因為不管是動態陣列還是全域性靜態陣列,在遞迴釋放呼叫排序函式時,都會儲存一份資料;如果是在排序函式中定義臨時陣列,那麼應該和前面的方法一樣的,因為是區域性臨時陣列,存放在棧空間,當該函式呼叫完後,會馬上釋放。所以我個人感覺這三種方法都差不多(如果是在歸併函式中定義的臨時陣列,則需要全部壓棧;而其他的就只需要壓入有用資料所佔的空間就可以) 執行結果:#include<stdio.h> #define LEN 12 // 巨集定義陣列的大小 static int tmp[LEN] = {0};// 設定臨時陣列 // 列印陣列 void print_array(int *array) { int index = 0; printf("\narray:\n"); for (; index < LEN; index++){ printf(" %d, ", *(array + index)); } printf("\n"); } // 把兩個有序的陣列排序成一個數組 void _mergeSort(int *array, int start, int middle, int end) { int first = start; int second = middle + 1; int index = start; while ((first <= middle) && (second <= end)){ if (array[first] >= array[second]) tmp[index++] = array[second++]; else tmp[index++] = array[first++]; } while(first <= middle) tmp[index++] = array[first++]; while(second <= end) tmp[index++] = array[second++]; for (first = start; first <= end; first++) array[first] = tmp[first]; } // 遞迴劃分陣列 void mergeSort(int *array, int start, int end) { if (start >= end) return; int middle = ((end + start) >> 1); mergeSort(array, start, middle);// 遞迴劃分左邊的陣列 mergeSort(array, middle+1, end);// 遞迴劃分右邊的陣列 _mergeSort(array, start, middle, end);// 對有序的兩個陣列進行合併成一個有序的陣列 } int main(void) { int array[LEN] = {2, 1, 4, 0, 12, 520, 2, 9, 5, 3, 13, 14}; print_array(array); mergeSort(array, 0, LEN-1); print_array(array); return 0; }