基礎的排序演算法
c++基礎
生成隨即測驗用例
int *generateRandomArray(int n, int rangeL, int rangeR) {
assert(rangeL <= rangeR);
int *arr = new int[n];
srand(time(NULL));
for (int i = 0; i < n; i++)
arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;
return arr;
}
生成近乎有序的測驗用例
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;
}
拷貝陣列并回傳新陣列
int *copyIntArray(int a[], int n){
int *arr = new int[n];
//* 在VS中, copy函式被認為是不安全的,此時可用for回圈:)
copy(a, a+n, arr);
return arr;
}
列印陣列內容
template<typename T>
void printArray(T arr[], int n) {
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
cout << endl;
return;
}
判斷陣列是否有序
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;
}
判斷排序正確性與排序時間
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();
assert(isSorted(arr, n));
cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;
return;
}
選擇排序
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] );
}
}
選擇排序測驗
int main() {
// 測驗模板函式,傳入整型陣列
int a[10] = {10,9,8,7,6,5,4,3,2,1};
selectionSort( a , 10 );
for( int i = 0 ; i < 10 ; i ++ )
cout<<a[i]<<" ";
cout<<endl;
// 測驗模板函式,傳入浮點數陣列
float b[4] = {4.4,3.3,2.2,1.1};
selectionSort(b,4);
for( int i = 0 ; i < 4 ; i ++ )
cout<<b[i]<<" ";
cout<<endl;
// 測驗模板函式,傳入字串陣列
string c[4] = {"D","C","B","A"};
selectionSort(c,4);
for( int i = 0 ; i < 4 ; i ++ )
cout<<c[i]<<" ";
cout<<endl;
return 0;
}
對選擇排序進行他優化(在每一輪中, 可以同時找到當前未處理元素的最大值和最小值)
template<typename T>
void selectionSort(T arr[], int n){
int left = 0, right = n - 1;
while(left < right){
int minIndex = left;
int maxIndex = right;
// 在每一輪查找時, 要保證arr[minIndex] <= arr[maxIndex]
if(arr[minIndex] > arr[maxIndex])
swap(arr[minIndex], arr[maxIndex]);
for(int i = left + 1 ; i < right; i ++)
if(arr[i] < arr[minIndex])
minIndex = i;
else if(arr[i] > arr[maxIndex])
maxIndex = i;
swap(arr[left], arr[minIndex]);
swap(arr[right], arr[maxIndex]);
left ++;
right --;
}
return;
}
插入排序
基本的插入排序(寫法二看著更像是高手,嘻嘻嘻)
template<typename T>
void insertionSort(T arr[], int n){
for( int i = 1 ; i < n ; i ++ ) {
// 尋找元素arr[i]合適的插入位置
// 寫法1
// for( int j = i ; j > 0 ; j-- )
// if( arr[j] < arr[j-1] )
// swap( arr[j] , arr[j-1] );
// else
// break;
// 寫法2
for( int j = i ; j > 0 && arr[j] < arr[j-1] ; j -- )
swap( arr[j] , arr[j-1] );
}
return;
}
改進插入排序(將交換改為賦值)
template<typename T>
void insertionSort(T arr[], int n){
for( int i = 1 ; i < n ; i ++ ) {
T e = arr[i];
int j; // j保存元素e應該插入的位置
for (j = i; j > 0 && arr[j-1] > e; j--)
arr[j] = arr[j-1];
arr[j] = e;
}
return;
}
冒泡排序
template<typename T>
void bubbleSort( T arr[] , int n){
bool swapped;
do{
swapped = false;
for( int i = 1 ; i < n ; i ++ )
if( arr[i-1] > arr[i] ){
swap( arr[i-1] , arr[i] );
swapped = true;
}
// 優化, 每一趟Bubble Sort都將最大的元素放在了最后的位置
// 所以下一次排序, 最后的元素可以不再考慮
n --;
}while(swapped);
}
冒泡排序的另一種優化
template<typename T>
void bubbleSort2( 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);
}
希爾排序
在插入排序的基礎上改變
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是這四種排序演算法中性能最優的排序演算法,以下是對20000個亂數的測驗
Selection Sort : 0.517969 s
Insertion Sort : 0.267192 s
Bubble Sort : 1.9947 s
Shell Sort : 0.003899 s
歸并排序
// 將arr[l...mid]和arr[mid+1...r]兩部分進行歸并
template<typename T>
void __merge(T arr[], int l, int mid, int r){
//* VS不支持動態長度陣列, 即不能使用 T aux[r-l+1]的方式申請aux的空間
//* 使用VS, 可以使用new的方式申請aux空間
//* 使用new申請空間, 不要忘了在__merge函式的最后, delete掉申請的空間:)
T aux[r-l+1];
//T *aux = new T[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 ++;
}
}
//delete[] aux;
}
// 遞回使用歸并排序,對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 );
}
比較InsertionSort和MergeSort兩種排序演算法的性能效率,整體而言, MergeSort的性能最優,Merge Sort是一個O(nlogn)復雜度的演算法,可以在1秒之內輕松處理100萬數量級的資料,注意:不要輕易嘗試使用SelectionSort, InsertionSort或者BubbleSort處理100萬級的資料,否則,你就見識了O(n^2)的演算法和O(nlogn)演算法的本質差異:)
對于近乎有序的陣列, 陣列越有序, InsertionSort的時間性能越趨近于O(n),所以可以嘗試, 當swapTimes(生成幾乎有序陣列時的引數,決定有序程度)比較大時, MergeSort更快,但是當swapTimes小到一定程度, InsertionSort變得比MergeSort快
對上面的排序進行優化
// 使用優化的歸并排序演算法, 對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 );
}
歸并排序進一步種優化
// 將arr[l...mid]和arr[mid+1...r]兩部分進行歸并
// 其中aux為完成merge程序所需要的輔助空間
template<typename T>
void __merge2(T arr[], T aux[], int l, int mid, int r){
// 由于aux的大小和arr一樣, 所以我們也不需要處理aux索引的偏移量
// 進一步節省了計算量:)
for( int i = l ; i <= r; i ++ )
aux[i] = 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]; j ++;
}
else if( j > r ){ // 如果右半部分元素已經全部處理完畢
arr[k] = aux[i]; i ++;
}
else if( aux[i] < aux[j] ) { // 左半部分所指元素 < 右半部分所指元素
arr[k] = aux[i]; i ++;
}
else{ // 左半部分所指元素 >= 右半部分所指元素
arr[k] = aux[j]; j ++;
}
}
}
// 使用優化的歸并排序演算法, 對arr[l...r]的范圍進行排序
// 其中aux為完成merge程序所需要的輔助空間
template<typename T>
void __mergeSort2(T arr[], T aux[], int l, int r){
// 對于小規模陣列, 使用插入排序
if( r - l <= 15 ){
insertionSort(arr, l, r);
return;
}
int mid = (l+r)/2;
__mergeSort2(arr, aux, l, mid);
__mergeSort2(arr, aux, mid+1, r);
// 對于arr[mid] <= arr[mid+1]的情況,不進行merge
// 對于近乎有序的陣列非常有效,但是對于一般情況,有一定的性能損失
if( arr[mid] > arr[mid+1] )
__merge2(arr, aux, l, mid, r);
}
template<typename T>
void mergeSort2(T arr[], int n){
// 在 mergeSort2中, 我們一次性申請aux空間,
// 并將這個輔助空間以引數形式傳遞給完成歸并排序的各個子函式
T *aux = new T[n];
__mergeSort2( arr , aux, 0 , n-1 );
delete[] aux; // 使用C++, new出來的空間不要忘記釋放掉:)
}
Merge Sort 2 只開辟了一次輔助空間, 之后將這個輔助空間以引數形式傳遞給完成歸并排序的其他子函式,Merge Sort 2的性能優于 Merge Sort
使用自底向上的歸并排序演算法
template <typename T>
void mergeSortBU(T arr[], int n){
// Merge Sort Bottom Up 無優化版本
for( int sz = 1; sz < n ; sz += sz )
for( int i = 0 ; i < n - sz ; i += sz+sz )
// 對 arr[i...i+sz-1] 和 arr[i+sz...i+2*sz-1] 進行歸并
__merge(arr, i, i+sz-1, min(i+sz+sz-1,n-1) );
}
進行優化
template <typename T>
void mergeSortBU(T arr[], int n){
// Merge Sort Bottom Up 優化
// 對于小陣列, 使用插入排序優化
for( int i = 0 ; i < n ; i += 16 )
insertionSort(arr,i,min(i+15,n-1));
for( int sz = 16; sz < n ; sz += sz )
for( int i = 0 ; i < n - sz ; i += sz+sz )
// 對于arr[mid] <= arr[mid+1]的情況,不進行merge
if( arr[i+sz-1] > arr[i+sz] )
__merge(arr, i, i+sz-1, min(i+sz+sz-1,n-1) );
}
Merge Sort BU 也是一個O(nlogn)復雜度的演算法,雖然只使用兩重for回圈,所以,Merge Sort BU也可以在1秒之內輕松處理100萬數量級的資料,注意:不要輕易根據回圈層數來判斷演算法的復雜度,Merge Sort BU就是一個反例
比較Merge Sort和Merge Sort Bottom Up兩種排序演算法的性能效率,整體而言, 兩種演算法的效率是差不多的,但是如果進行仔細測驗, 自底向上的歸并排序會略勝一籌,
總體來說, Merge Sort BU 比 Merge Sort 快一些,但優化后, 二者的性能差距不明顯,
快速排序
// 對arr[l...r]部分進行partition操作
// 回傳p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p]
template <typename T>
int __partition(T arr[], int l, int r){
T v = arr[l];
int j = l; // arr[l+1...j] < v ; arr[j+1...i) > v
for( int i = l + 1 ; i <= r ; i ++ )
if( arr[i] < v ){
j ++;
swap( arr[j] , arr[i] );
}
swap( arr[l] , arr[j]);
return j;
}
// 對arr[l...r]部分進行快速排序
template <typename T>
void __quickSort(T arr[], int l, int r){
if( l >= r )
return;
int p = __partition(arr, l, r);
__quickSort(arr, l, p-1 );
__quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n){
__quickSort(arr, 0, n-1);
}
比較Merge Sort和Quick Sort兩種排序演算法的性能效率,兩種排序演算法雖然都是O(nlogn)級別的, 但是Quick Sort演算法有常數級的優勢,Quick Sort要比Merge Sort快, 即使對Merge Sort進行了優化,是對于近乎有序的陣列, 快速排序演算法退化成了O(n^2)級別的演算法,下面對其進行優化
template <typename T>
int _partition(T arr[], int l, int r){
// 隨機在arr[l...r]的范圍中, 選擇一個數值作為標定點pivot
swap( arr[l] , arr[rand()%(r-l+1)+l] );
T v = arr[l];
int j = l;
for( int i = l + 1 ; i <= r ; i ++ )
if( arr[i] < v ){
j ++;
swap( arr[j] , arr[i] );
}
swap( arr[l] , arr[j]);
return j;
}
// 對arr[l...r]部分進行快速排序
template <typename T>
void _quickSort(T arr[], int l, int r){
// 對于小規模陣列, 使用插入排序進行優化
if( r - l <= 15 ){
insertionSort(arr,l,r);
return;
}
int p = _partition(arr, l, r);
_quickSort(arr, l, p-1 );
_quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n){
srand(time(NULL));
_quickSort(arr, 0, n-1);
}
加入了隨機選擇標定點的步驟后, 我們的快速排序可以輕松處理近乎有序的陣列,但是對于近乎有序的陣列, 其效率比優化后的歸并排序要低, 但完全再容忍范圍里,但此時, 對于含有大量相同元素的陣列, 我們的快速排序演算法再次退化成了O(n^2)級別的演算法,
雙路快速排序
// 雙路快速排序的partition
// 回傳p, 使得arr[l...p-1] <= arr[p] ; arr[p+1...r] >= arr[p]
// 雙路快排處理的元素正好等于arr[p]的時候要注意,詳見下面的注釋:)
template <typename T>
int _partition2(T arr[], int l, int r){
// 隨機在arr[l...r]的范圍中, 選擇一個數值作為標定點pivot
swap( arr[l] , arr[rand()%(r-l+1)+l] );
T v = arr[l];
// arr[l+1...i) <= v; arr(j...r] >= v
int i = l+1, j = r;
while( true ){
// 注意這里的邊界, arr[i] < v, 不能是arr[i] <= v
while( i <= r && arr[i] < v )
i ++;
// 注意這里的邊界, arr[j] > v, 不能是arr[j] >= v
while( j >= l+1 && arr[j] > v )
j --;
// 對于上面的兩個邊界的設定, 有的同學在課程的問答區有很好的回答:)
if( i > j )
break;
swap( arr[i] , arr[j] );
i ++;
j --;
}
swap( arr[l] , arr[j]);
return j;
}
// 對arr[l...r]部分進行快速排序
template <typename T>
void _quickSort(T arr[], int l, int r){
// 對于小規模陣列, 使用插入排序進行優化
if( r - l <= 15 ){
insertionSort(arr,l,r);
return;
}
// 呼叫雙路快速排序的partition
int p = _partition2(arr, l, r);
_quickSort(arr, l, p-1 );
_quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n){
srand(time(NULL));
_quickSort(arr, 0, n-1);
}
雙路快速排序演算法也可以輕松處理近乎有序的陣列,使用雙快速排序后, 我們的快速排序演算法可以輕松的處理包含大量元素的陣列
三路快速排序(可以對比單路快速排序)
// 遞回的三路快速排序演算法
template <typename T>
void __quickSort3Ways(T arr[], int l, int r){
// 對于小規模陣列, 使用插入排序進行優化
if( r - l <= 15 ){
insertionSort(arr,l,r);
return;
}
// 隨機在arr[l...r]的范圍中, 選擇一個數值作為標定點pivot
swap( arr[l], arr[rand()%(r-l+1)+l ] );
T v = arr[l];
int lt = l; // arr[l+1...lt] < v
int gt = r + 1; // arr[gt...r] > v
int i = l+1; // arr[lt+1...i) == v
while( i < gt ){
if( arr[i] < v ){
swap( arr[i], arr[lt+1]);
i ++;
lt ++;
}
else if( arr[i] > v ){
swap( arr[i], arr[gt-1]);
gt --;
}
else{ // arr[i] == v
i ++;
}
}
swap( arr[l] , arr[lt] );
__quickSort3Ways(arr, l, lt-1);
__quickSort3Ways(arr, gt, r);
}
template <typename T>
void quickSort3Ways(T arr[], int n){
srand(time(NULL));
__quickSort3Ways( arr, 0, n-1);
}
比較Merge Sort和雙路快速排序和三路快排三種排序演算法的性能效率,對于包含有大量重復資料的陣列, 三路快排有巨大的優勢,對于一般性的隨機陣列和近乎有序的陣列, 三路快排的效率雖然不是最優的, 但是是在非常可以接受的范圍里,因此, 在一些語言中, 三路快排是默認的語言庫函式中使用的排序演算法,比如Java:)
逆序數對(歸并排序)
// 計算逆序數對的結果以long long回傳
// 對于一個大小為N的陣列, 其最大的逆序數對個數為 N*(N-1)/2, 非常容易產生整型溢位
// merge函式求出在arr[l...mid]和arr[mid+1...r]有序的基礎上, arr[l...r]的逆序數對個數
long long __merge( int arr[], int l, int mid, int r){
int *aux = new int[r-l+1];
for( int i = l ; i <= r ; i ++ )
aux[i-l] = arr[i];
// 初始化逆序數對個數 res = 0
long long res = 0;
// 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
int j = l, k = mid + 1;
for( int i = l ; i <= r ; i ++ ){
if( j > mid ){ // 如果左半部分元素已經全部處理完畢
arr[i] = aux[k-l];
k ++;
}
else if( k > r ){ // 如果右半部分元素已經全部處理完畢
arr[i] = aux[j-l];
j ++;
}
else if( aux[j-l] <= aux[k-l] ){ // 左半部分所指元素 <= 右半部分所指元素
arr[i] = aux[j-l];
j ++;
}
else{ // 右半部分所指元素 < 左半部分所指元素
arr[i] = aux[k-l];
k ++;
// 此時, 因為右半部分k所指的元素小
// 這個元素和左半部分的所有未處理的元素都構成了逆序數對
// 左半部分此時未處理的元素個數為 mid - j + 1
res += (long long)(mid - j + 1);
}
}
delete[] aux;
return res;
}
// 求arr[l..r]范圍的逆序數對個數
// 思考: 歸并排序的優化可否用于求逆序數對的演算法? :)
long long __inversionCount(int arr[], int l, int r){
if( l >= r )
return 0;
int mid = l + (r-l)/2;
// 求出 arr[l...mid] 范圍的逆序數
long long res1 = __inversionCount( arr, l, mid);
// 求出 arr[mid+1...r] 范圍的逆序數
long long res2 = __inversionCount( arr, mid+1, r);
return res1 + res2 + __merge( arr, l, mid, r);
}
// 遞回求arr的逆序數對個數
long long inversionCount(int arr[], int n){
return __inversionCount(arr, 0, n-1);
}
尋找arr陣列中第k小的元素(快速排序)
main.cpp
#include <iostream>
#include <ctime>
#include <cassert>
#include <algorithm>
#include "TestHelper.h"
using namespace std;
// partition 程序, 和快排的partition一樣
template <typename T>
int __partition( T arr[], int l, int r ){
int p = rand()%(r-l+1) + l;
swap( arr[l] , arr[p] );
int j = l; //[l+1...j] < p ; [lt+1..i) > p
for( int i = l + 1 ; i <= r ; i ++ )
if( arr[i] < arr[l] )
swap(arr[i], arr[++j]);
swap(arr[l], arr[j]);
return j;
}
// 求出arr[l...r]范圍里第k小的數
template <typename T>
T __selection( T arr[], int l, int r, int k ){
if( l == r )
return arr[l];
// partition之后, arr[p]的正確位置就在索引p上
int p = __partition( arr, l, r );
if( k == p ) // 如果 k == p, 直接回傳arr[p]
return arr[p];
else if( k < p ) // 如果 k < p, 只需要在arr[l...p-1]中找第k小元素即可
return __selection( arr, l, p-1, k);
else // 如果 k > p, 則需要在arr[p+1...r]中找第k-p-1小元素
// 注意: 由于我們傳入__selection的依然是arr, 而不是arr[p+1...r],
// 所以傳入的最后一個引數依然是k, 而不是k-p-1
return __selection( arr, p+1, r, k );
}
// 尋找arr陣列中第k小的元素
// 注意: 在我們的演算法中, k是從0開始索引的, 即最小的元素是第0小元素, 以此類推
// 如果希望我們的演算法中k的語意是從1開始的, 只需要在整個邏輯開始進行k--即可, 可以參考selection2
template <typename T>
T selection(T arr[], int n, int k) {
assert( k >= 0 && k < n );
srand(time(NULL));
return __selection(arr, 0, n - 1, k);
}
// 尋找arr陣列中第k小的元素, k從1開始索引, 即最小元素是第1小元素, 以此類推
template <typename T>
T selection2(T arr[], int n, int k) {
return selection(arr, n, k - 1);
}
// 測驗 selection演算法
int main() {
// 生成一個大小為n, 包含0...n-1這n個元素的隨機陣列arr
int n = 10000;
int* arr = TestHelper::generateOrderedArray(n);
TestHelper::shuffleArray(arr, n);
// 驗證selection演算法, 對arr陣列求第i小元素, 應該為i
for( int i = 0 ; i < n ; i ++ ){
assert( selection(arr, n, i) == i );
cout<<"test "<<i<<" complete."<<endl;
}
cout<<"Test selection completed."<<endl;
delete[] arr;
cout << endl;
// 驗證selection2演算法
arr = TestHelper::generateOrderedArray(n);
TestHelper::shuffleArray(arr, n);
// 對arr陣列求第i小元素, 應該為i - 1 (在selection2中, 第k小元素的k是從1索引的)
for( int i = 1 ; i <= n ; i ++ ){
assert( selection2(arr, n, i) == i - 1 );
cout<<"test "<<i<<" complete."<<endl;
}
cout<<"Test selection2 completed."<<endl;
delete[] arr;
return 0;
}
TestHelper.h
#ifndef OPTIONAL_3_SELECTION_TESTHELPER_H
#define OPTIONAL_3_SELECTION_TESTHELPER_H
#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;
namespace TestHelper {
// 生成一個完全有序的陣列
int *generateOrderedArray(int n) {
int *arr = new int[n];
for (int i = 0; i < n; i++)
arr[i] = i;
return arr;
}
// 將陣列arr隨機化
void shuffleArray(int arr[], int n){
srand(time(NULL));
for (int i = 0; i < n; i++) {
int j = rand() % (n-i)+i;
swap( arr[i], arr[j]);
}
}
}
#endif //OPTIONAL_3_SELECTION_TESTHELPER_H
總結
Merge Sort BU 比 Merge Sort 快一些,但優化后, 二者的性能差距不明顯,
Shell Sort雖然慢于高級的排序方式, 但仍然是非常有競爭力的一種排序演算法,其所花費的時間完全在可以容忍的范圍內, 遠不像O(n^2)的排序演算法, 在資料量較大的時候無法忍受,同時, Shell Sort實作簡單, 只使用回圈的方式解決排序問題, 不需要實作遞回, 不占用系統占空間, 也不依賴亂數,所以, 如果演算法實作所使用的環境不利于實作復雜的排序演算法, 或者在專案工程的測驗階段, 完全可以暫時使用Shell Sort來進行排序任務:)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/60077.html
標籤:其他
上一篇:CF1324E Sleeping Schedule(基礎dp)
下一篇:為什么作業系統能安裝到虛擬機
