1. 程式人生 > >O(n*logn)級別的演算法之一(歸併排序)

O(n*logn)級別的演算法之一(歸併排序)

測試用例:

#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;
}

測試結果:
在這裡插入圖片描述