四個O(n^2)級別的排序效能測試
測試環境為DEV-C++,並且選擇排序,插入排序,氣泡排序,均為優化後的,若想了解具體優化過程,請參照:https://blog.csdn.net/qq_40164152
測試用例:
#ifndef OPTIONAL_02_SHELL_SORT_SORTTESTHELPER_H #define OPTIONAL_02_SHELL_SORT_SORTTESTHELPER_H #include <iostream> #include <algorithm> #include <ctime> #include <string> #include <cassert> usingnamespace 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 //OPTIONAL_02_SHELL_SORT_SORTTESTHELPER_H
選擇排序:
基本思想:每一趟在n-i+1(i=1,2,…,n-1)個記錄中選取關鍵字最小的記錄作為有序序列中第i個記錄。直接選擇排序和直接插入排序類似,都將資料分為有序區和無序區,所不同的是直接插入排序是將無序區的第一個元素直接插入到有序區以形成一個更大的有序區,而直接選擇排序是從無序區選一個最小的元素直接放到有序區的最後。
#ifndef OPTIONAL_02_SHELL_SORT_SELECTIONSORT_H #define OPTIONAL_02_SHELL_SORT_SELECTIONSORT_H #include <iostream> #include <algorithm> using namespace std; template<typename T> void selectionSort(T arr[], int n){ for(int i = 0 ; i < n ; i ++){ int minIndex = i; for( int j = i + 1 ; j < n ; j ++ ) if( arr[j] < arr[minIndex] ) minIndex = j; swap( arr[i] , arr[minIndex] ); } } #endif //OPTIONAL_02_SHELL_SORT_SELECTIONSORT_H
插入排序:
基本思想:將一個記錄插入到已排好序的有序表中,從而得到一個新的、記錄數增1的有序表。
時間複雜度為O(n^2),若待排記錄序列為正序,時間複雜度可提高至O(n);空間上只需要一個記錄的輔助空間。
#ifndef OPTIONAL_02_SHELL_SORT_INSERTIONSORT_H #define OPTIONAL_02_SHELL_SORT_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; } #endif //OPTIONAL_02_SHELL_SORT_INSERTIONSORT_H
氣泡排序:
基本思想:首先將第一個記錄的關鍵字和第二個記錄的關鍵字進行比較,若為逆序,則將兩個記錄交換之,然後比較第二個記錄和第三個記錄的關鍵字。依次類推,直至第n-1個記錄和第n個記錄的關鍵字進行過比較為止。上述過程稱做第一趟氣泡排序,其結果使得關鍵字最大的記錄被安置到最後一個記錄的位置上。然後進行第二趟氣泡排序,對前n-1個記錄進行同樣操作,其結果是使關鍵字次大的記錄被安置到第n-1個記錄的位置上。一般地,第i趟氣泡排序是從1到n-i+1依次比較相鄰兩個關鍵字,並在“逆序”時交換相鄰記錄,其結果是這n-i+1個記錄中關鍵字最大的記錄被交換到第n-i+1的位置上。判別氣泡排序結束的條件應該是“在一趟排序過程中沒有進行過交換記錄的操作”。
#ifndef OPTIONAL_02_SHELL_SORT_BUBBLESORT_H #define OPTIONAL_02_SHELL_SORT_BUBBLESORT_H#include <iostream> #include <algorithm> using namespace std; template<typename T> void bubbleSort( T arr[] , int n){ int newn; // 使用newn進行優化 do{ newn = 0; for( int i = 1 ; i < n ; i ++ ) if( arr[i-1] > arr[i] ){ swap( arr[i-1] , arr[i] ); // 記錄最後一次的交換位置,在此之後的元素在下一輪掃描中均不考慮 newn = i; } n = newn; }while(newn > 0); } #endif //OPTIONAL_02_SHELL_SORT_BUBBLESORT_H
希爾排序與測試:
基本思想:先將整個待排記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。可以看出希爾排序其實只是改進了的插入排序,因此上面的插入排序也被稱為直接插入排序。
特點:子序列的構成不是簡單地“逐段分割”,而是將相隔某個“增量”的記錄組成一個子序列。它通過比較相距一定間隔的元素來工作;各趟比較所用的距離隨著演算法的進行而減小,直到只比較相鄰元素的最後一趟排序為止。
#include <iostream> #include "SortTestHelper.h" #include "SelectionSort.h" #include "InsertionSort.h" #include "BubbleSort.h" using namespace std; template<typename T> void shellSort(T arr[], int n){ // 計算 increment sequence: 1, 4, 13, 40, 121, 364, 1093... int h = 1; while( h < n/3 ) h = 3 * h + 1; while( h >= 1 ){ // h-sort the array for( int i = h ; i < n ; i ++ ){ // 對 arr[i], arr[i-h], arr[i-2*h], arr[i-3*h]... 使用插入排序 T e = arr[i]; int j; for( j = i ; j >= h && e < arr[j-h] ; j -= h ) arr[j] = arr[j-h]; arr[j] = e; } h /= 3; } } // 比較SelectionSort, InsertionSort和BubbleSort和ShellSort四種排序演算法的效能效率 // ShellSort是這四種排序演算法中效能最優的排序演算法 int main() { int n = 20000; // 測試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); int *arr4 = SortTestHelper::copyIntArray(arr1, n); SortTestHelper::testSort("Selection Sort", selectionSort, arr1, n); SortTestHelper::testSort("Insertion Sort", insertionSort, arr2, n); SortTestHelper::testSort("Bubble Sort", bubbleSort, arr3, n); SortTestHelper::testSort("Shell Sort", shellSort, arr4, n); delete[] arr1; delete[] arr2; delete[] arr3; delete[] arr4; cout<<endl; // 測試2 測試近乎有序的陣列,只有100個數字無序 int swapTimes = 100; 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); arr4 = SortTestHelper::copyIntArray(arr1, n); SortTestHelper::testSort("Selection Sort", selectionSort, arr1, n); SortTestHelper::testSort("Insertion Sort", insertionSort, arr2, n); SortTestHelper::testSort("Bubble Sort", bubbleSort, arr3, n); SortTestHelper::testSort("Shell Sort", shellSort, arr4, n); delete[] arr1; delete[] arr2; delete[] arr3; delete[] arr4; return 0; }
測試結果: