时间复杂度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倍