1. 程式人生 > >歸併排序演算法的優化——學習筆記

歸併排序演算法的優化——學習筆記

歸併排序是一種穩定排序,時間複雜度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較大時應選用二分插排。

插入排序和二分插入排序就不奉上程式碼了,同學們可以練練手自己實現一下。