数据结构之时间复杂度o(n^2)的排序算法

时间复杂度o(n^2)的排序算法包括选择排序、插入排序、冒泡排序
c++语法实现如下:

1.选择排序

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

2.插入排序

template<typename T>
void insertionSort(T arr[],int n){
    for(int i=1;i<n;i++){
        T temp = arr[i];
        int j;
        for(j=i-1;j>=0 && arr[j]>temp;j--)
            arr[j+1] = arr[j];//使用赋值操作  
        arr[j+1] = temp;  	//相对于直接调用swap()减少了赋值操作次数和函数调用
    }
}

优化处:

将内层for循环中的swap(arr[j],arr[j+1]) 替换为直接赋值操作
例如231
某时刻i=2进入内循环 1和3比较 若直接采用swap操作 1和3交换后 2和1再次交换 完成排序 共执行了6个赋值操作 两个函数调用
若采用上图方式 1和3比较 执行arr[2]=3 再执行arr[1]=2 退出循环后 执行arr[0]=1 共执行3个赋值操作

特性

插入排序有个特殊之处:对于完全有序的一串元素进行排序 插入排序的时间复杂度进化到O(n)这个级别
原因在于 内层for循环可以提前退出
这一特性可以用于归并排序、快速排序的优化中

3.冒泡排序

先放一个大学课本上的传统bubblSort

template<typename T>
void bubbleSort1(T arr[],int n){
    for(int i=0;i<n;i++){ //比较趟数
        for(int j=0;j<n-1-i;j++) //每趟比较次数
            if(arr[j]>arr[j+1])
                swap(arr[j],arr[j+1]);
    }
}

大一时 c语言老师说每个人都把这个背下来(: 对于刚刚接触的我们来说 肯定不能完全理解其中的算法思想

缺点

上面这个写法 对于一串完全有序的元素 不能够像插入排序一样 提前退出 浪费了很多不必要的时间

优化

template<typename T>
void bubbleSort(T arr[],int n){
    do{
        int num = 0;
        for(int i=0;i<n-1;i++){
            if(arr[i]>arr[i+1]){
                swap(arr[i],arr[i+1]);
                num = i+1;
            }
        }
        n = num;
    }while(n>0);
}

[^1]通过定义一个标记num 标记传入的数组是否有序 每趟比较开始时初始化为0
[^2]对于完全有序的一串元素 for循环中永远不会发生swap 遍历一遍则退出do-while
[^3]如果发生了swap 说明该趟遍历中 将一个最大值交换到了最后 则在下一趟遍历中 无需考虑该元素

4.性能测试

测试工具类

#include<iostream>
#include<cassert>
#include<ctime>
#include<stdlib.h>
using namespace std;

namespace SortTestHelper{

    /**
        generateRandomArray
    **/
    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;
    }

    //printArray
    template<typename T>
    void printArray(T arr[],int n){
        for(int i=0;i<n;i++){
            cout<<arr[i] << " ";
        }
        cout<<endl;
    }

    //test sort result
    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(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 ;
    }


    int *copyIntArr(int arr[],int n){
        int *a = new int[n];
        copy(arr,arr+n,a);
        return a;
    }

    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 randX = rand()%n;
            int randY = rand()%n;
            swap(arr[randX],arr[randY]);
        }
        return arr;
    }

}

测试结果

在这里插入图片描述
对于完全有序的数组 插入排序和优化后的冒泡排序的时间复杂度都进化为纳秒级别

在这里插入图片描述
n扩大2倍,运行时间近似扩大为4倍

下一篇 介绍 插入排序和希尔排序

发布了16 篇原创文章 · 获赞 1 · 访问量 1735

猜你喜欢

转载自blog.csdn.net/qq_42584241/article/details/104566028