原理簡述
單個元素肯定有序;
歸併排序採用分治思想,分而治之:
將待排序數組劃分爲n等分,每份長度爲1個元素,則 n份全部有序;
再一生二,二生四,逐步兩兩元素有序的區間,歸一合併成1個有序區間;
最終會歸併出整一個數組元素有序的結果.
歸併排序有兩種實現方式:
(1)自頂向下遞歸調用實現;
(2)自底向上迭代執行實現.
遞歸調用(自頂向下,Top-down)
//左右雙閉區間[low,high];
//歸併過程;
template<typename T>
void __Merge(T* arr, T* tempArr, const int low, const int mid, const int high) {
//臨時存儲空間需要在每一次歸併之前更新數據,使得已排序的元素得以存儲;
for (int index = low; index <= high; ++index) {
tempArr[index] = arr[index];
}
int i = low;//左區間[low,mid]起點;
int j = mid + 1;//右區間[mid+1,high]起點;
//歸併過程,使用索引k記錄已排好序的最後元素位置;
for (int k = low; k <= high; ++k) {
if (i > mid) {//左區間已遍歷完;
arr[k] = tempArr[j++];
}
else if (j > high) {//右區間已遍歷完;
arr[k] = tempArr[i++];
}
else if (tempArr[i] <= tempArr[j]) {//相等時取左側元素以保持排序的穩定性;
arr[k] = tempArr[i++];
}
else {//左大右小時,才取右區間元素;
arr[k] = tempArr[j++];
}
////輸出並理解中間過程;
//RandomArrayFuntionHelper::PrintArray(arr, low, high+1);
//RandomArrayFuntionHelper::PrintArray(tempArr, low, high+1);
//std::cout << " ------\n";
}
}
//分治過程;
template<typename T>
void __MergeSort(T* arr, T* tempArr, int low, int high) {//左閉右開區間,[low,high);
//if (low < high) {
//通過修改條件並通過else代碼,使用直接插入排序優化;
if (low + 15 < high) {
//int mid = low + (high - low) / 2;
int mid = low + ((high - low) >> 1);
//分(二分、折半);
__MergeSort(arr, tempArr, low, mid);
__MergeSort(arr, tempArr, mid + 1, high);
//治(歸併、合一);
if (arr[mid] > arr[mid + 1]) {
__Merge(arr, tempArr, low, mid, high);
//下一行代碼爲適得其反的優化,https://blog.csdn.net/tinyDolphin/article/details/78457343;
//__Merge(tempArr, arr, low, mid, high);
}
}
else {
DirectInsertionSort(arr, low, high);
//BinaryInsertSort(arr, low, high);
//經測試,百萬數量級時:剩餘 10~50 元素左右時使用,效果較佳(治在if優化之前15最佳,加了if條件之後50最好);
//千萬數量級時:剩餘 30 元素左右時使用,效果較佳;
//二分插入的優化比直接插入的優化稍微遜色,也可以適當使剩餘元素變爲最佳狀態時的 3/2倍左右,二分的效果更佳;
}
}
//遞歸調用(Top-down);
template<typename T>
//void DataSort<T>::MergeSortTD(T* arr, const std::size_t n) {
void MergeSortTD(T* arr, const std::size_t n) {
//臨時數組空間;
T *tempArr = new T[n];
//僅處理元素索引區間(相當於左右全閉區間);
__MergeSort(arr, tempArr, 0, n - 1);
delete[] tempArr;
}
溫馨提示:
代碼中間採用了小規模時,直接通過直接插入排序算法實現優化;
該部分代碼鏈接在我的另外一篇文章裏,鏈接如下:
//重載直接插入排序;
template <typename T>
void DirectInsertionSort(T* arr, const int low, const int high) {//high極可能會傳入負數值,故而不可使用無符號數;
assert(arr);
//i索引遍歷待排序元素;
for (int i = low + 1; i <= high; ++i) {
T temp = arr[i];
int j;
for (j = i; j > low && arr[j - 1] > temp; --j) {
arr[j] = arr[j - 1];//把前邊的元素往後挪,用這一操作替換掉每一次的數值交換swap,能減少開銷;
}
//找到合適插入位置j,第二次循環提前終止;
if (j != i) {
arr[j] = temp;
}
}
}
關於歸併排序算法的具體優化思想,可以參考文章:
迭代實現(自底向上,Bottom-up)
需要使用頭文件:
#include <algorithm>
//歸併過程;
template<typename T>
void __Merge(T* arr, T* tempArr, const int low, const int mid, const int high) {
//臨時存儲空間需要在每一次歸併之前更新數據,使得已排序的元素得以存儲;
for (int index = low; index <= high; ++index) {
tempArr[index] = arr[index];
}
int i = low;//左區間[low,mid]起點;
int j = mid + 1;//右區間[mid+1,high]起點;
//歸併過程,使用索引k記錄已排好序的最後元素位置;
for (int k = low; k <= high; ++k) {
if (i > mid) {//左區間已遍歷完;
arr[k] = tempArr[j++];
}
else if (j > high) {//右區間已遍歷完;
arr[k] = tempArr[i++];
}
else if (tempArr[i] <= tempArr[j]) {//相等時取左側元素以保持排序的穩定性;
arr[k] = tempArr[i++];
}
else {//左大右小時,才取右區間元素;
arr[k] = tempArr[j++];
}
////輸出並理解中間過程;
//RandomArrayFuntionHelper::PrintArray(arr, low, high+1);
//RandomArrayFuntionHelper::PrintArray(tempArr, low, high+1);
//std::cout << " ------\n";
}
}
//歸併排序(自底向上迭代版)(Bottom-up);
template<typename T>
//void DataSort<T>::MergeSortBU(T* arr, const std::size_t n) {
void MergeSortBU(T* arr, const std::size_t n) {
T* tempArr = new T[n];
//最開始的小規模數組,使用直接插入排序優化;
const std::size_t littleSize = 16;
for (std::size_t index = 0; index < n; index += littleSize) {
DirectInsertionSort(arr, index, std::min(index + littleSize - 1, n - 1));
}
//第 i ( i = 0,1,2 …… )輪要歸併已分別各自有序的兩個數組範圍是;
//arr[ index + i * sz, index + (i+1) * sz - 1];
//arr[ index + (i+1) * sz, index + (i+2) * sz - 1];
for (std::size_t sz = littleSize; sz < n; sz += sz) {//分組長度size(元素個數);
for (std::size_t index = 0; index + sz < n; index += sz + sz) {
if (arr[index + sz - 1] > arr[index + sz]) {
__Merge(arr, tempArr, index, index + sz - 1, std::min(index + sz + sz - 1, n - 1));
}
}
}
delete[] tempArr;
}
參考材料
歸併排序優化思想解說,可以參考:
歸併排序算法的動畫過程演示,可以參考:
排序算法過程演示
交流方式
QQ —— 2636105163(南國爛柯者)
溫馨提示:
轉載請註明出處!!
文章最後更新時間: 2020年3月29日23:24:16