Algorithms--Sort Base

Keywords: shell less Java

Basic sorting algorithm

c++ Foundation

Generate Instant Test Cases

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

Generate near-ordered test cases

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

Copy the array and return the new array

 int *copyIntArray(int a[], int n){

        int *arr = new int[n];
        //*In VS, the copy function is considered unsafe and a for loop is available:)
        copy(a, a+n, arr);
        return arr;
    }

Print Array Content

template<typename T>
    void printArray(T arr[], int n) {

        for (int i = 0; i < n; i++)
            cout << arr[i] << " ";
        cout << endl;

        return;
    }

Determine if the array is in order

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

Judging Sorting Correctness and Sorting Time

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

Select Sort

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] );
    }
}

Select Sort Test

int main() {

    // Test the template function, passing in an integer array
    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;

    // Test the template function, passing in a floating-point array
    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;

    // Test the template function, passing in an array of strings
    string c[4] = {"D","C","B","A"};
    selectionSort(c,4);
    for( int i = 0 ; i < 4 ; i ++ )
        cout<<c[i]<<" ";
    cout<<endl;

    return 0;
}

Optimize for alternative ordering (you can find both the maximum and minimum values of the currently unprocessed elements in each round)

template<typename T>
void selectionSort(T arr[], int n){

    int left = 0, right = n - 1;
    while(left < right){
        int minIndex = left;
        int maxIndex = right;

        // Make sure 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;
}

Insert Sort

Basic insertion sort (Write 2 looks more like a master, hip-hop)

template<typename T>
void insertionSort(T arr[], int n){

    for( int i = 1 ; i < n ; i ++ ) {

        // Find the appropriate insertion position for the element arr[i]
        // Writing 1
//        for( int j = i ; j > 0 ; j-- )
//            if( arr[j] < arr[j-1] )
//                swap( arr[j] , arr[j-1] );
//            else
//                break;

        // Writing 2
        for( int j = i ; j > 0 && arr[j] < arr[j-1] ; j -- )
            swap( arr[j] , arr[j-1] );

    }

    return;
}

Improve insert sorting (change exchange to assignment)

template<typename T>
void insertionSort(T arr[], int n){

    for( int i = 1 ; i < n ; i ++ ) {

        T e = arr[i];
        int j; // JSave where element e should be inserted
        for (j = i; j > 0 && arr[j-1] > e; j--)
            arr[j] = arr[j-1];
        arr[j] = e;
    }

    return;
}

Bubble sort

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;

            }

        // Optimize, each Bubble Sort puts the largest element at the end
        // So next time you sort, the last element can be left out of consideration
        n --;

    }while(swapped);
}

Another optimization for bubble sorting

template<typename T>
void bubbleSort2( T arr[] , int n){

    int newn; // Optimize with newn

    do{
        newn = 0;
        for( int i = 1 ; i < n ; i ++ )
            if( arr[i-1] > arr[i] ){
                swap( arr[i-1] , arr[i] );

                // Record the last swap position, after which elements will not be considered in the next scan
                newn = i;
            }
        n = newn;
    }while(newn > 0);
}

Shell Sort

Change based on insert sort

template<typename T>
void shellSort(T arr[], int n){

    // Calculate 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 ++ ){

            // Use insert sort for 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;
    }
}

Compare the performance efficiency of four sort algorithms: SelectionSort, InsertionSort, BubbleSort and ShellSort. ShellSort is the best of the four sort algorithms. Here is a test of 20,000 random numbers.

Selection Sort : 0.517969 s
Insertion Sort : 0.267192 s
Bubble Sort : 1.9947 s
Shell Sort : 0.003899 s

Merge Sort

// Merge arr[l...mid] and arr[mid+1...r]
template<typename  T>
void __merge(T arr[], int l, int mid, int r){

    //* VS does not support dynamic length arrays, that is, you cannot request aux space using T aux[r-l+1]
    //*With VS, you can request aux space using the new method
    //*Use the new request space, don't forget delete drops the application space at the end of the u merge function:)
    T aux[r-l+1];
    //T *aux = new T[r-l+1];

    for( int i = l ; i <= r; i ++ )
        aux[i-l] = arr[i];

    // Initialize, i points to the starting index position l of the left half; j points to the starting index position mid+1 of the right half
    int i = l, j = mid+1;
    for( int k = l ; k <= r; k ++ ){

        if( i > mid ){  // If the left half of the element has been completely processed
            arr[k] = aux[j-l]; j ++;
        }
        else if( j > r ){  // If the right half of the element has been completely processed
            arr[k] = aux[i-l]; i ++;
        }
        else if( aux[i-l] < aux[j-l] ) {  // Left half refers to element <right half refers to element
            arr[k] = aux[i-l]; i ++;
        }
        else{  // Left half refers to element >=Right half refers to element
            arr[k] = aux[j-l]; j ++;
        }
    }

    //delete[] aux;
}

// Recursively uses recursion and sorting to sort the range of 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 );
}

Comparing the performance efficiency of InsertionSort and MergeSort sorting algorithms, MergeSort has the best overall performance. Merge Sort is an O (nlogn) complexity algorithm that can easily process data of orders of magnitude 1 million in one second. Note: Do not easily attempt to use SelectionSort.InsertionSort or BubbleSort processes 1 million levels of data, otherwise you see the essential differences between O(n^2) and O(nlogn) algorithms:)

For nearly ordered arrays, the more ordered the array, the faster the time performance of InsertionSort approaches O(n), so try to make MergeSort faster when swapTimes (the parameters that determine the degree of ordering when generating an almost ordered array) is larger, but when swapTimes is smaller to a certain extent, InsertionSort becomes faster than MergeSort

Optimize the sorting above

// Sort the range of arr[l...r] using an optimized merge sort algorithm
template<typename T>
void __mergeSort2(T arr[], int l, int r){

    // Optimize 2: Use insert sort for small arrays
    if( r - l <= 15 ){
        insertionSort(arr, l, r);
        return;
    }

    int mid = (l+r)/2;
    __mergeSort2(arr, l, mid);
    __mergeSort2(arr, mid+1, r);

    // Optimize 1: No merge for arr[mid] <= arr[mid+1]
    // Very effective for near-ordered arrays, but has a performance penalty for general cases
    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 );
}

Merge Sort Further Seed Optimization

// Merge arr[l...mid] and arr[mid+1...r]
// Where aux is the auxiliary space needed to complete the merge process
template<typename  T>
void __merge2(T arr[], T aux[], int l, int mid, int r){

    // Since aux is the same size as arr, we do not need to handle the offset of the aux index
    // Further savings in computation:)
    for( int i = l ; i <= r; i ++ )
        aux[i] = arr[i];

    // Initialize, i points to the starting index position l of the left half; j points to the starting index position mid+1 of the right half
    int i = l, j = mid+1;
    for( int k = l ; k <= r; k ++ ){

        if( i > mid ){  // If the left half of the element has been completely processed
            arr[k] = aux[j]; j ++;
        }
        else if( j > r ){  // If the right half of the element has been completely processed
            arr[k] = aux[i]; i ++;
        }
        else if( aux[i] < aux[j] ) {  // Left half refers to element <right half refers to element
            arr[k] = aux[i]; i ++;
        }
        else{  // Left half refers to element >=Right half refers to element
            arr[k] = aux[j]; j ++;
        }
    }

}

// Sort the range of arr[l...r] using an optimized merge sort algorithm
// Where aux is the auxiliary space needed to complete the merge process
template<typename T>
void __mergeSort2(T arr[], T aux[], int l, int r){

    // For small arrays, use insert sort
    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);

    // For arr[mid] <= arr[mid+1], merge is not used
    // Very effective for near-ordered arrays, but has a performance penalty for general cases
    if( arr[mid] > arr[mid+1] )
        __merge2(arr, aux, l, mid, r);
}


template<typename T>
void mergeSort2(T arr[], int n){

    // In mergeSort2, we apply for aux space once.
    // And pass this auxiliary space as a parameter to the subfunctions that complete the merge sort
    T *aux = new T[n];

    __mergeSort2( arr , aux, 0 , n-1 );

    delete[] aux;   // With C++, don't forget to free up the space that Newcomes out:)
}

Merge Sort 2 only opens up an auxiliary space once and then passes it as a parameter to other subfunctions that complete the merge sort. Merge Sort 2 performs better than Merge Sort

Use bottom-up merge sort algorithm

template <typename T>
void mergeSortBU(T arr[], int n){

    // Merge Sort Bottom Up No Optimized Version
   for( int sz = 1; sz < n ; sz += sz )
        for( int i = 0 ; i < n - sz ; i += sz+sz )
         // Merge arr[i...i+sz-1] and arr[i+sz...i+2*sz-1]
         __merge(arr, i, i+sz-1, min(i+sz+sz-1,n-1) );

}

Optimize

template <typename T>
void mergeSortBU(T arr[], int n){

    // Merge Sort Bottom Up Optimization
    // For decimal arrays, use insert sort optimization
    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 )
            // For arr[mid] <= arr[mid+1], merge is not used
            if( arr[i+sz-1] > arr[i+sz] )
                __merge(arr, i, i+sz-1, min(i+sz+sz-1,n-1) );

}

Merge Sort BU is also an O(nlogn) complexity algorithm. Although only two for loops are used, Merge Sort BU can easily process data of 1 million orders of magnitude in 1 second. Note: Do not easily judge the complexity of the algorithm based on the number of loops, Merge Sort BU is an example of this.

Comparing the performance efficiency of Merge Sort and Merge Sort Bottom Up sorting algorithms, the efficiency of the two sorting algorithms is similar overall.But if tested carefully, the bottom-up merge sort will be a little better.

Overall, Merge Sort BU is faster than Merge Sort.But after optimization, the performance gap between them is not obvious.

Quick Sort

// partition operation on arr[l...r] section
// Returns P such that 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;
}

// Quickly sort the arr[l...r] section
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);
}

Comparing the performance efficiency of Merge Sort and Quick Sort sorting algorithms. Although both sorting algorithms are O(nlogn) level, Quick Sort algorithm has the advantage of constant level. Quick Sort is faster than Merge Sort. Even if Merge Sort is optimized, the fast sorting algorithm degenerates to O(n^2) level algorithm, and is optimized below.

template <typename T>
int _partition(T arr[], int l, int r){

    // Randomly within the range of arr[l...r], select a numeric value as the pivot of the calibration point
    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;
}

// Quickly sort the arr[l...r] section
template <typename T>
void _quickSort(T arr[], int l, int r){

    // For small arrays, use insert sort to optimize
    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);
}

By adding the step of random selection of calibration points, our quick sorting can easily process an almost ordered array, but it is less efficient than the optimized merge sorting, but it is completely within the tolerance range. At this time, for arrays with a large number of identical elements, our fast sorting algorithm degenerates again to an O(n^2) level algorithm.

Two-way Quick Sort

// Two-way Quick Sort partition
// Returns P such that arr[l...p-1] <= arr[p]; arr[p+1...r] >= arr[p]
// Note that the elements processed by the two-way queue are exactly equal to arr[p], as detailed in the note below:)
template <typename T>
int _partition2(T arr[], int l, int r){

    // Randomly within the range of arr[l...r], select a numeric value as the pivot of the calibration point
    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 ){
        // Notice the boundary here, arr[i] < v, can't be arr[i] <= V
        while( i <= r && arr[i] < v )
            i ++;

        // Notice the boundary here, arr[j] > v, can't be arr[j] >= V
        while( j >= l+1 && arr[j] > v )
            j --;

        // For the above two boundary settings, some students have good answers in the Q&A area of the course:)

        if( i > j )
            break;

        swap( arr[i] , arr[j] );
        i ++;
        j --;
    }

    swap( arr[l] , arr[j]);

    return j;
}

// Quickly sort the arr[l...r] section
template <typename T>
void _quickSort(T arr[], int l, int r){

    // For small arrays, use insert sort to optimize
    if( r - l <= 15 ){
        insertionSort(arr,l,r);
        return;
    }

    // Invoke partition for two-way quick sorting
    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);
}

The two-way quick sorting algorithm can also easily handle near-ordered arrays. With the use of double quick sorting, our quick sorting algorithm can easily handle arrays with a large number of elements

Three-way quick sorting (one-way quick sorting can be compared)

// Recursive three-way fast sorting algorithm
template <typename T>
void __quickSort3Ways(T arr[], int l, int r){

    // For small arrays, use insert sort to optimize
    if( r - l <= 15 ){
        insertionSort(arr,l,r);
        return;
    }

    // Randomly within the range of arr[l...r], select a numeric value as the pivot of the calibration point
    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);
}

Comparing the performance efficiency of Merge Sort and two-way and three-way fast sorting algorithms, three-way fast sorting has a huge advantage for arrays with a large amount of repetitive data. For general random arrays and nearly ordered arrays, the efficiency of three-way fast sorting is not optimal, but is within a very acceptable range. Therefore, in some languages,Three-way fast queuing is the sort algorithm used in the default library function.For example, Java:)

Reverse ordinal pair (merge sort)

// Returns the result of calculating a pair of inverse ordinal numbers as long long
// For an array of size N, the maximum number of pairs of inverse ordinal numbers is N*(N-1)/2, which is very prone to integer overflow

// The merge function finds the number of pairs of inverse ordinal numbers of arr[l...r] on the basis of the order of arr[l...mid] and arr[mid+1...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];

    // Initialize inverse ordinal pair number res = 0
    long long res = 0;
    // Initialize, i points to the starting index position l of the left half; j points to the starting index position mid+1 of the right half
    int j = l, k = mid + 1;
    for( int i = l ; i <= r ; i ++ ){
        if( j > mid ){ // If the left half of the element has been completely processed
            arr[i] = aux[k-l];
            k ++;
        }
        else if( k > r ){ // If the right half of the element has been completely processed
            arr[i] = aux[j-l];
            j ++;
        }
        else if( aux[j-l] <= aux[k-l] ){ // Left half refers to element <=Right half refers to element
            arr[i] = aux[j-l];
            j ++;
        }
        else{ // Right half refers to element <left half refers to element
            arr[i] = aux[k-l];
            k ++;
            // At this point, because the element referred to in the right half of k is small
            // This element and all the unhandled elements in the left half constitute an inverse ordinal pair
            // The number of elements left half unhandled at this time is mid - j + 1
            res += (long long)(mid - j + 1);
        }
    }

    delete[] aux;

    return res;
}

// Count the inverse ordinal pairs of arr[l..r] ranges
// Think: Can the optimization of merge sort be used for the algorithm of inverting ordinal pairs?
long long __inversionCount(int arr[], int l, int r){

    if( l >= r )
        return 0;

    int mid = l + (r-l)/2;

    // Find the inverse ordinal number of the range arr[l...mid]
    long long res1 = __inversionCount( arr, l, mid);
    // Find the inverse ordinal number of the range arr[mid+1...r]
    long long res2 = __inversionCount( arr, mid+1, r);

    return res1 + res2 + __merge( arr, l, mid, r);
}

// Number of inverse ordinal pairs for recursive arr
long long inversionCount(int arr[], int n){

    return __inversionCount(arr, 0, n-1);
}

Find the kth smallest element in the arr array (quick sort)

main.cpp

#include <iostream>
#include <ctime>
#include <cassert>
#include <algorithm>
#include "TestHelper.h"

using namespace std;

// Partition process, just like fast 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;
}

// Find the k th smallest number in the range arr[l...r]
template <typename T>
T __selection( T arr[], int l, int r, int k ){

    if( l == r )
        return arr[l];

    // After partition, the correct position of arr[p] is on index p
    int p = __partition( arr, l, r );

    if( k == p )    // Return arr[p] directly if k == p
        return arr[p];
    else if( k < p )    // If k < p, simply look for the kth smallest element in arr[l...p-1]
        return __selection( arr, l, p-1, k);
    else // If k > p, you need to find the k-p-1 small element in arr[p+1...r]
         // Note: Since we still passed in arr, not arr[p+1...r],
         //       So the last parameter passed in is still k, not k-p-1
        return __selection( arr, p+1, r, k );
}

// Find the k th smallest element in the arr array
// Note: In our algorithm, k is indexed from 0, that is, the smallest element is the 0th smallest element, and so on
// If you want the meaning of K in our algorithm to start with 1, you only need to start with k--in the whole logic, you can refer to 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);
}

// Find the smallest k element in the arr array, k is indexed from the beginning, that is, the smallest element is the first smallest element, and so on
template <typename T>
T selection2(T arr[], int n, int k) {

    return selection(arr, n, k - 1);
}


// Testing the selection algorithm
int main() {

    // Generates a random array arr of size N containing 0...n-1 elements
    int n = 10000;
    int* arr = TestHelper::generateOrderedArray(n);
    TestHelper::shuffleArray(arr, n);

    // Verify the selection algorithm, find the ith smallest element of the arr array, should be 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;

    // Verify selection2 algorithm
    arr = TestHelper::generateOrderedArray(n);
    TestHelper::shuffleArray(arr, n);

    // Small element i of arr array should be i - 1 (in selection2, the k of small element k is indexed from 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 {

    // Generate a completely ordered array
    int *generateOrderedArray(int n) {

        int *arr = new int[n];
        for (int i = 0; i < n; i++)
            arr[i] = i;

        return arr;
    }

    // Randomize Array
    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

summary

Merge Sort BU is faster than Merge Sort.But after optimization, the performance gap between them is not obvious.

Shell Sort is a very competitive sorting algorithm, although slower than the advanced sorting method. It takes more time than O(n^2) sorting algorithm can tolerate in large amounts of data. At the same time, Shell Sort is simple to implement, uses only a circular method to solve sorting problems, does not need recursion.It does not take up system space or rely on random numbers, so if the environment in which the algorithm is implemented is not conducive to the implementation of complex sorting algorithms, or during the testing phase of a project project project, it is entirely possible to temporarily use Shell Sort for sorting tasks:)

Posted by artied on Wed, 15 Apr 2020 17:55:30 -0700