歸併排序演算法的優化——學習筆記
阿新 • • 發佈:2019-01-10
歸併排序是一種穩定排序,時間複雜度O(nlogn),空間複雜度O(n)。
那麼已經非常高效的歸併排序是否還能再優化呢?當然是可以的,timsort就是在歸併排序上改進的一種高階排序方式,現在廣泛運用在如python,Java等主流語言中。timsort是高效的,完整的演算法是相當複雜的,因此我這裡主要根據timsort提兩個優化思路。
先附上歸併排序的演算法:
//num -- 待排序的陣列 //start -- 排序的起點 //end -- 排序陣列最後一位 void MergeSort(int *nums, int start, int end) { //判斷起點是否小於終點 if (end-start<2) { return; } //進行分割操作 int mid = (start + end) / 2; MergeSort(nums, start, mid); MergeSort(nums, mid , end); //歸併兩個子列 Merge(nums, start, mid, end); }
1、避免無用的歸併
看例子:
{1,2} {7,8}
當兩個子序列已經排好序的狀態時,我們就不必再做無用的合併操作,此時加一句判斷即可,如下:
void MergeSort(int *nums, int start, int end) { if (end-start<2) { return; } int mid = (start + end) / 2; MergeSort(nums, start, mid); MergeSort(nums, mid , end); //判斷兩個子列是否已經排好序 if (nums[mid] < nums[mid - 1]) { Merge(nums, start, mid, end); } }
2、短序列用插入排序
歸併排序每次都要遞迴到長度為1的子序列,當進行大量資料排序的時候,每次遞迴都是要佔用系統棧空間的。為了避免過度呼叫棧空間,可以設定一個最小歸併長度L,當子序列的長度小於L時,我們就呼叫其他排序,如插入排序,插入排序在小陣列的排序效率上要比歸併排序稍好。
void MergeSort(int *nums, int start, int end) { //當長度小於L時改用插入排序 if (end-start<L) { InsertSort(nums,start,end); return; } int mid = (start + end) / 2; MergeSort(nums, start, mid); MergeSort(nums, mid , end); if (nums[mid] < nums[mid - 1]) { Merge(nums, start, mid, end); } }
3、插入排序用二分插入排序效率更高
二分插入排序是基於二分查詢改進的插入排序,效率要更優於插入排序。在陣列非常短的時候二分插排和普通插排效率接近,而當陣列數量增加後二分插排要明顯優於普通插排。
當然這就要看L的取值了。當L取值較小時,運用普通插排即可,而L較大時應選用二分插排。
插入排序和二分插入排序就不奉上程式碼了,同學們可以練練手自己實現一下。