排序算法总结-选择排序、插入排序、归并排序和快速排序

  前言:

  感觉好久没写博客了,十月份的计划是:要开始深入攻克数据结构和算法,耽误好久了,这都月末了,抓紧时间又学习了一波,赶紧来分享了一下,使用的语言是C++,最开始学数据结构一定要用C,掌握扎实之后,想学算法,用C++比较好,C封装没有那么好,写起来没有那么容易了。

  一、准备工作

  这部分会封装一些接口,如生成数组、测试排序算法执行时间等,便于比较和调试。封装在.h中,如下:

  

《排序算法总结-选择排序、插入排序、归并排序和快速排序》
《排序算法总结-选择排序、插入排序、归并排序和快速排序》

#ifndef SORTTESTHELPER_H_
#define SORTTESTHELPER_H_

#include <iostream>
#include <ctime>
#include <cassert>
#include <string>

using namespace std;

namespace SortTestHelper {

    // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR]
    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;
    }
    //copy一个数组
    int *copyIntArray(int a[], int n) {

        int *arr = new int[n];
        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;
    }

};
#endif //SORTTESTHELPER_H_

View Code

 

  二、选择排序

  选择相对简单,就是两次循环,假如排序:从小到大,每次循环把小的移到前面,程序如下:

#include <iostream>
#include "test.h"

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

int main() {

    int n = 10000;
    int *arr = SortTestHelper::generateRandomArray(n, 0, n);
    SortTestHelper::testSort("Selection Sort", selectionSort, arr, n);
    delete[] arr;

    system("pause");

    return 0;
}

  三、插入排序

  就是将较小的数据插入到前面,程序如下:

  

《排序算法总结-选择排序、插入排序、归并排序和快速排序》
《排序算法总结-选择排序、插入排序、归并排序和快速排序》

#include <iostream>
#include <algorithm>
//#include "SortTestHelper.h"
//#include "SelectionSort.h"
#include"test.h"

using namespace std;

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

        // 写法3
        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;
}

int main() {

    int n = 10000;
    
    // 测试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);

    SortTestHelper::testSort("Insertion Sort", insertionSort,arr1,n);
    //SortTestHelper::testSort("Selection Sort", selectionSort,arr2,n);

    delete[] arr1;
    delete[] arr2;

    cout<<endl;


    // 测试2 有序性更强的测试
    cout<<"Test for More Ordered Random Array, size = "<<n<<", random range [0, 3]"<<endl;
    arr1 = SortTestHelper::generateRandomArray(n,0,3);
    arr2 = SortTestHelper::copyIntArray(arr1, n);

    SortTestHelper::testSort("Insertion Sort", insertionSort,arr1,n);
    //SortTestHelper::testSort("Selection Sort", selectionSort,arr2,n);

    delete[] arr1;
    delete[] arr2;

    cout<<endl;


    // 测试3 测试近乎有序的数组
    int swapTimes = 100;
    cout<<"Test for Random Nearly Ordered Array, size = "<<n<<", swap time = "<<swapTimes<<endl;
    arr1 = SortTestHelper::generateNearlyOrderedArray(n,swapTimes);
    arr2 = SortTestHelper::copyIntArray(arr1, n);

    SortTestHelper::testSort("Insertion Sort", insertionSort,arr1,n);
    //SortTestHelper::testSort("Selection Sort", selectionSort,arr2,n);

    delete(arr1);
    delete(arr2);

    system("pause");

    return 0;
}

View Code

  四、归并排序

  归并排序就比较复杂了,时间复杂度也从前两种的O(n^2)变为O(nlogn),代码如下:

  MergeSort.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) {

    // 经测试,传递aux数组的性能效果并不好
    T aux[r - l + 1];
    for (int i = l; i <= r; i++)
        aux[i - l] = arr[i];

    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

View Code

  merge.cpp如下:

  

《排序算法总结-选择排序、插入排序、归并排序和快速排序》
《排序算法总结-选择排序、插入排序、归并排序和快速排序》

#include <iostream>
#include "test.h"
//#include "InsertionSort.h"
#include "MergeSort.h"

using namespace std;


// 递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort2(T arr[], int l, int r) {

    // 对于小规模数组,使用插入排序
    if (r - l <= 15) {
        insertionSort(arr, l, r);
        return;
    }

    int mid = (l + r) / 2;
    __mergeSort2(arr, l, mid);
    __mergeSort2(arr, mid + 1, r);
    // 对于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;
    cout << "Test for Random 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;
}

View Code

  五、快速排序

  

《排序算法总结-选择排序、插入排序、归并排序和快速排序》
《排序算法总结-选择排序、插入排序、归并排序和快速排序》

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

    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 ){
        while( i <= r && arr[i] < v )
            i ++;

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

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

//    if( l >= r )
//        return;
    if( r - l <= 15 ){
        insertionSort(arr,l,r);
        return;
    }

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

View Code

  总结:

  数据结构和算法是基本功,必须深入学习,也是大厂总考的原因

  

  

  

点赞