O(n*logn)級別的演算法之一(歸併排序)
阿新 • • 發佈:2018-12-22
測試用例:
#ifndef INC_02_MERGE_SORT_SORTTESTHELPER_H #define INC_02_MERGE_SORT_SORTTESTHELPER_H #include <iostream> #include <algorithm> #include <string> #include <ctime> #include <cassert> using namespace std; namespace SortTestHelper { // 生成有n個元素的隨機陣列,每個元素的隨機範圍為[rangeL, rangeR] int *generateRandomArray(int n, int range_l, int range_r) { int *arr = new int[n]; srand(time(NULL)); for (int i = 0; i < n; i++) arr[i] = rand() % (range_r - range_l + 1) + range_l; return arr; } // 生成一個近乎有序的陣列 // 首先生成一個含有[0...n-1]的完全有序陣列, 之後隨機交換swapTimes對資料 // swapTimes定義了陣列的無序程度 int *generateNearlyOrderedArray(int n, int swapTimes){ int *arr = new int[n]; for(int i = 0 ; i < n ; i ++ ) arr[i] = i; srand(time(NULL)); for( int i = 0 ; i < swapTimes ; i ++ ){ int posx = rand()%n; int posy = rand()%n; swap( arr[posx] , arr[posy] ); } return arr; } // 拷貝整型陣列a中的所有元素到一個新的陣列, 並返回新的陣列 int *copyIntArray(int a[], int n){ int *arr = new int[n]; //* 在VS中, copy函式被認為是不安全的, 請大家手動寫一遍for迴圈:) copy(a, a+n, arr); return arr; } // 列印arr陣列的所有內容 template<typename T> void printArray(T arr[], int n) { for (int i = 0; i < n; i++) cout << arr[i] << " "; cout << endl; return; } // 判斷arr陣列是否有序 template<typename T> bool isSorted(T arr[], int n) { for (int i = 0; i < n - 1; i++) if (arr[i] > arr[i + 1]) return false; return true; } // 測試sort排序演算法排序arr陣列所得到結果的正確性和演算法執行時間 template<typename T> void testSort(const string &sortName, void (*sort)(T[], int), T arr[], int n) { clock_t startTime = clock(); sort(arr, n); clock_t endTime = clock(); cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s"<<endl; assert(isSorted(arr, n)); return; } }; #endif //INC_02_MERGE_SORT_SORTTESTHELPER_H
插入排序(因為等下優化時需要用到):
#ifndef INC_03_MERGE_SORT_ADVANCE_INSERTIONSORT_H #define INC_03_MERGE_SORT_ADVANCE_INSERTIONSORT_H #include <iostream> #include <algorithm> using namespace std; template<typename T> void insertionSort(T arr[], int n){ for( int i = 1 ; i < n ; i ++ ) { T e = arr[i]; int j; for (j = i; j > 0 && arr[j-1] > e; j--) arr[j] = arr[j-1]; arr[j] = e; } return; } // 對arr[l...r]範圍的陣列進行插入排序 template<typename T> void insertionSort(T arr[], int l, int r){ for( int i = l+1 ; i <= r ; i ++ ) { T e = arr[i]; int j; for (j = i; j > l && arr[j-1] > e; j--) arr[j] = arr[j-1]; arr[j] = e; } return; } #endif //INC_03_MERGE_SORT_ADVANCE_INSERTIONSORT_H
一般歸併排序:
#ifndef INC_03_MERGE_SORT_ADVANCE_MERGESORT_H #define INC_03_MERGE_SORT_ADVANCE_MERGESORT_H #include <iostream> using namespace std; // 將arr[l...mid]和arr[mid+1...r]兩部分進行歸併 template<typename T> void __merge(T arr[], int l, int mid, int r){ T aux[r-l+1]; for( int i = l ; i <= r; i ++ ) aux[i-l] = arr[i]; // 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1 int i = l, j = mid+1; for( int k = l ; k <= r; k ++ ){ if( i > mid ){ // 如果左半部分元素已經全部處理完畢 arr[k] = aux[j-l]; j ++; } else if( j > r ){ // 如果右半部分元素已經全部處理完畢 arr[k] = aux[i-l]; i ++; } else if( aux[i-l] < aux[j-l] ) { // 左半部分所指元素 < 右半部分所指元素 arr[k] = aux[i-l]; i ++; } else{ // 左半部分所指元素 >= 右半部分所指元素 arr[k] = aux[j-l]; j ++; } } } // 遞迴使用歸併排序,對arr[l...r]的範圍進行排序 template<typename T> void __mergeSort(T arr[], int l, int r){ if( l >= r ) return; int mid = (l+r)/2; __mergeSort(arr, l, mid); __mergeSort(arr, mid+1, r); __merge(arr, l, mid, r); } template<typename T> void mergeSort(T arr[], int n){ __mergeSort( arr , 0 , n-1 ); } #endif //INC_03_MERGE_SORT_ADVANCE_MERGESORT_H
優化後的歸併排序:
#include <iostream>
#include "SortTestHelper.h"
#include "InsertionSort.h"
#include "MergeSort.h"
using namespace std;
// 使用優化的歸併排序演算法, 對arr[l...r]的範圍進行排序
template<typename T>
void __mergeSort2(T arr[], int l, int r){
// 優化2: 對於小規模陣列, 使用插入排序
if( r - l <= 15 ){
insertionSort(arr, l, r);
return;
}
int mid = (l+r)/2;
__mergeSort2(arr, l, mid);
__mergeSort2(arr, mid+1, r);
// 優化1: 對於arr[mid] <= arr[mid+1]的情況,不進行merge
// 對於近乎有序的陣列非常有效,但是對於一般情況,有一定的效能損失
if( arr[mid] > arr[mid+1] )
__merge(arr, l, mid, r);
}
template<typename T>
void mergeSort2(T arr[], int n){
__mergeSort2( arr , 0 , n-1 );
}
int main() {
int n = 50000;
// 測試1 一般性測試
cout<<"Test for random array, size = "<<n<<", random range [0, "<<n<<"]"<<endl;
int* arr1 = SortTestHelper::generateRandomArray (n,0,n);
int* arr2 = SortTestHelper::copyIntArray(arr1, n);
int* arr3 = SortTestHelper::copyIntArray(arr1, n);
SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n);
SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);
SortTestHelper::testSort("Merge Sort 2", mergeSort2, arr3, n);
delete[] arr1;
delete[] arr2;
delete[] arr3;
cout<<endl;
// 測試2 測試近乎有序的陣列
int swapTimes = 10;
assert( swapTimes >= 0 );
cout<<"Test for nearly ordered array, size = "<<n<<", swap time = "<<swapTimes<<endl;
arr1 = SortTestHelper::generateNearlyOrderedArray(n,swapTimes);
arr2 = SortTestHelper::copyIntArray(arr1, n);
arr3 = SortTestHelper::copyIntArray(arr1, n);
SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n);
SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);
SortTestHelper::testSort("Merge Sort 2", mergeSort2, arr3, n);
delete[] arr1;
delete[] arr2;
delete[] arr3;
return 0;
}
測試結果: