当前位置: 首页 > news >正文

迄今为止的排序算法总结

迄今为止的排序算法总结

  • 7.10 迄今为止的排序算法总结
    • 复杂度和稳定性
    • 时间复杂度测试程序
      • sortAlgorithm.h
      • sortAlgorithm.cpp
      • test.cpp
    • 时间复杂度测试结果

7.10 迄今为止的排序算法总结

复杂度和稳定性

排序算法平均情况最好情况最坏情况稳定性空间复杂度
选择排序O(n^2)O(n^2)O(n^2)不稳定(有的书或资料说稳定,以自己的资料为准)O(1)
堆排序O(nlogn)O(nlogn)O(nlogn)不稳定O(1)(新开辟堆来处理数据的堆排序为O(n))
直接插入排序O(n^2)O(n)O(n^2)稳定O(1)
希尔排序O(nlogn)~O(n^{2})O(n^{1.3})O(n^{2})不稳定O(1)
冒泡排序O(n^2)(这里的所有排序算法里效率最差的)O(n)O(n^2)(这里的所有排序算法里效率最差的)稳定O(1)
快速排序O(nlogn)O(nlogn)O(n^2)不稳定O(logn)~O(n)
归并排序O(nlogn)O(nlogn)O(nlogn)稳定(主要取决于比较规则,若是数据大小,则要用>=或<=)O(n)
计数排序O(n+Range)(理论上所有排序算法中最快的,但局限性非常大)O(n+Range)(理论上所有排序算法中最快的,但局限性非常大)O(n+Range)(理论上所有排序算法中最快的,但局限性非常大)稳定O(Range)(取决于数据中的极端值)

时间复杂度测试程序

这是一套用于测试在10个数据到100000个数据的情况下,不同排序算法(包括他们的优化版本、未优化版本)的用时的c++代码。计时用的是chrono库的高精度计时,输出的单位是秒。

sortAlgorithm.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>typedef int Datatype;//直接选择排序
void selectSort(Datatype a[], int n);//经优化的选择排序,每次同时选出最小的和最大的
void selectSortplus(Datatype* a, int n);//堆排序
void heapSort(Datatype* a, int n);//拷贝方式的堆排序
void heapSortCopy(Datatype* a, int n);//直接插入排序未优化
void insertSort(Datatype* a, int n);//直接插入排序小优化
void insertSortPlus(Datatype* a, int n);//希尔排序,gap=gap/2
void shellSort(Datatype* a, int n);//希尔排序,gap=gap/3+1
void shellSort2(Datatype* a, int n);//冒泡排序,因为性能实在太差,只上优化版本
void bubbleSort(Datatype* a, int n);//快速排序,hoare版本
void quickSortHoare(Datatype* a, int left, int right);//快速排序,挖坑法
void quickSortHole(Datatype* a, int left, int right);//快速排序,前后指针
void quickSortDPoint(Datatype* a, int left, int right);//快速排序,三路划分优化
void quickSortTRoute(int* a, int begin, int end);//快排的非递归实现,Hoare版本
void quickSortNonRHoare(Datatype* a, int left, int right);//快排的非递归实现,挖坑法
void quickSortNonRHole(Datatype* a, int left, int right);//快排的非递归实现,前后指针
void quickSortNonRDPoint(Datatype* a, int left, int right);//归并排序,无小区间优化
void mergeSort(Datatype* a, int n);//归并排序,小区间优化
void mergeSortPlus(Datatype* a, int n);//归并排序,非递归
void mergeSortNonR(Datatype* a, int n);//计数排序
void countSort(Datatype* a, int n);

sortAlgorithm.cpp

#define _CRT_SECURE_NO_WARNINGS 1#include"sortAlgorithm.h"//交换函数
void Swap(Datatype* a, Datatype* b) {Datatype t = *a;*a = *b;*b = t;
}//直接选择排序
void selectSort(Datatype a[], int n) {int i = 0, j = 0, k = 0;for (i = 0; i < n; i++) {k = i;//每次更新第一个(或最后一个)元素的位置 for (j = i + 1; j < n; j++)if (a[j] < a[k])//遍历寻找最小值k = j;if (k != i) {//找到的最小值没有放在第一个位置int temp = a[k];a[k] = a[i];a[i] = temp;}}
}//经优化的选择排序,每次同时选出最小的和最大的
void selectSortplus(Datatype* a, int n) {int begin = 0, end = n - 1;//起标记作用的两个变量while (begin < end) {//能不能加等于,取决于交换数据时用的方法int maxi = begin, mini = begin;for (int i = begin; i <= end; i++) {if (a[i] > a[maxi])maxi = i;if (a[i] < a[mini])mini = i;}Swap(&a[begin], &a[mini]);if (begin == maxi)//处理begin和maxi重叠时的情况maxi = mini;Swap(&a[end], &a[maxi]);++begin;//双指针思路--end;}
}//大堆向下调整
void adjustMaxDown(Datatype* a, int n, int parent) {int child = parent * 2 + 1;while (child < n) {if (child + 1 < n)if (a[child] < a[child + 1])++child;if (a[child] > a[parent]) {Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else break;}
}//堆排序,
void heapSort(Datatype* a, int n) {int i = 0;//向下调整建堆,从倒数第一个父结点开始for (i = (n - 1 - 1) / 2; i >= 0; i--)adjustMaxDown(a, n, i);int end = n - 1;while (end > 0) {int tmp = a[0];a[0] = a[end];a[end] = tmp;adjustMaxDown(a, end, 0);--end;}
}//拷贝方式的堆排序
void heapSortCopy(Datatype* a, int n) {//2*N*logN//创建一个临时的堆Datatype* hp = (Datatype*)malloc(sizeof(Datatype) * n);if (hp == NULL)return;int i = 0;for (i = 0; i < n; i++)hp[i] = a[i];for (i = (n - 1 - 1) / 2; i >= 0; i--)adjustMaxDown(hp, n, i);int cnt = 0, hpCap = n;for (i = n - 1; i >= 0; i--) {a[i] = hp[0];Swap(&hp[0], &hp[hpCap - 1]);hpCap--;adjustMaxDown(hp, hpCap, 0);}free(hp);
}//直接插入排序未优化
void insertSort(Datatype* a, int n) {for (int i = 0, j, k; i < n; i++) {for (j = i - 1; j >= 0; j--)//这里并没有挪动操作,所以是浪费了部分时间if (a[j] < a[i])//修改这里的比较运算符为<=能使算法不稳定break;if (j != i - 1) {Datatype temp = a[i];for (k = i - 1; k > j; k--)a[k + 1] = a[k];a[k + 1] = temp;}}
}//直接插入排序小优化
void insertSortPlus(Datatype* a, int n) {for (int i = 0; i < n - 1; ++i) {// [0, end] 有序,插入tmp依旧有序int end = i;//int end;表示单趟,int end=i;和for表示整个过程Datatype tmp = a[i + 1];while (end >= 0) {if (a[end] > tmp) {//修改这里的比较运算符为>=能使算法不稳定a[end + 1] = a[end];--end;}elsebreak;}a[end + 1] = tmp;}
}//希尔排序,gap=gap/2
void shellSort(Datatype* a, int n) {int gap = n;while (gap > 1) {gap = gap / 2;//缩小增量int i = 0;for (i = 0; i < n - gap; ++i) {//插入排序int end = i;int tmp = a[end + gap];while (end >= 0) {if (a[end] > tmp) {a[end + gap] = a[end];end -= gap;}else break;}a[end + gap] = tmp;}}
}//希尔排序,gap=gap/3+1
void shellSort2(Datatype* a, int n) {int gap = n;while (gap > 1) {gap = gap / 3 + 1;//+1可以保证最后一次一定是1int i = 0;for (i = 0; i < n - gap; ++i) {//插入排序int end = i;Datatype tmp = a[end + gap];while (end >= 0) {if (a[end] > tmp) {a[end + gap] = a[end];end -= gap;}else break;}a[end + gap] = tmp;}}
}//冒泡排序,因为性能实在太差,只上优化版本
void bubbleSort(Datatype* a, int n) {int i = 0, j = 0;for (i = 0; i < n; i++) {//表示这是第i+1轮排序int judg = 0;for (j = 1; j < n - i; j++)//表示正在比较的数据if (a[j - 1] > a[j]) {Swap(&a[j - 1], &a[j]);judg = 1;}if (judg == 0) break;}
}int partSortHoare(Datatype* a, int left, int right) {int keyi = left;//keyi记录下标, key记录值。while (left < right) {//相遇时结束循环// 右边找小while (left < right && a[right] >= a[keyi])//利用c语言的短路特性去判断--right;// 左边找大while (left < right && a[left] <= a[keyi])++left;Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);//相遇了就交换return left;
}//快速排序,hoare版本
void quickSortHoare(Datatype* a, int left, int right) {if (right - left <= 0)//区间只有1个值或区间不存在时没必要继续分return;// 按照基准值对array数组的 [left, right)区间中的元素进行划分int keyi = partSortHoare(a, left, right);// 划分成功后以keyi为边界形成了左右两部分 [left, keyi) 和 [keyi+1, right)// 递归排[left, div)quickSortHoare(a, left, keyi - 1);// 递归排[div+1, right)quickSortHoare(a, keyi + 1, right);
}int partSortHole(Datatype* a, int left, int right) {int key = a[left];int hole = left;//坑while (left < right) {// 右边找小while (left < right && a[right] >= key)--right;a[hole] = a[right];hole = right;//找到数据后就交换,形成新坑// 左边找大while (left < right && a[left] <= key)++left;a[hole] = a[left];hole = left;}a[hole] = key;//相遇位置即为最后一个坑的位置return hole;
}//快速排序,挖坑法
void quickSortHole(Datatype* a, int left, int right) {if (right - left <= 0)//区间只有1个值或区间不存在时没必要继续分return;// 按照基准值对array数组的 [left, right)区间中的元素进行划分int keyi = partSortHole(a, left, right);// 划分成功后以keyi为边界形成了左右两部分 [left, keyi) 和 [keyi+1, right)// 递归排[left, div)quickSortHole(a, left, keyi - 1);// 递归排[div+1, right)quickSortHole(a, keyi + 1, right);
}int partSortDPoint(Datatype* a, int left, int right) {int prev = left;int cur = left + 1;int keyi = left;while (cur <= right) {if (a[cur] < a[keyi] && ++prev != cur)//prev和cur相等时无意义Swap(&a[prev], &a[cur]);++cur;}Swap(&a[prev], &a[keyi]);//keyi = prev; return keyi;return prev;
}//快速排序,前后指针
void quickSortDPoint(Datatype* a, int left, int right) {if (right - left <= 0)//区间只有1个值或区间不存在时没必要继续分return;// 按照基准值对array数组的 [left, right)区间中的元素进行划分int keyi = partSortDPoint(a, left, right);// 划分成功后以keyi为边界形成了左右两部分 [left, keyi) 和 [keyi+1, right)// 递归排[left, div)quickSortDPoint(a, left, keyi - 1);// 递归排[div+1, right)quickSortDPoint(a, keyi + 1, right);
}//快排的三路划分再优化方案。
void quickSortTRoute(Datatype* a, int begin, int end) {if (begin >= end)return;int left = begin;int right = end;int cur = left + 1;Datatype key = a[left];while (cur <= right) {if (a[cur] < key) {Swap(&a[cur], &a[left]);++left;++cur;}else if (a[cur] > key) {Swap(&a[cur], &a[right]);--right;}else {++cur;}}quickSortTRoute(a, begin, left - 1);quickSortTRoute(a, right + 1, end);
}//快排的非递归实现,Hoare版本
void quickSortNonRHoare(Datatype* a, int left, int right) {int* st = (int*)malloc(sizeof(int) * 100 * 10//floor(log(right - left + 1) / log(2)) * 3);//栈开100个空间后就能处理平时99%的数据排列//开1000个是因为极端情况下越界严重if (st == NULL) {printf("栈空间申请失败\n");return;}int top = 0;//一个栈顶变量和一个数组构成的简易栈st[top++] = right;st[top++] = left;while (top != 0) {int l = st[top - 1];--top;int r = st[top - 1];--top;int keyi = partSortHoare(a, l, r);//单趟排序if (keyi + 1 < r) {st[top++] = r;//右边先入栈st[top++] = keyi + 1;}if (l < keyi - 1) {st[top++] = keyi - 1;//左边后入栈st[top++] = l;}}free(st);
}//快排的非递归实现,挖坑法
void quickSortNonRHole(Datatype* a, int left, int right) {int* st = (int*)malloc(sizeof(int) * 100 * 10//floor(log(right - left + 1) / log(2)) * 3);//栈开100个空间后就能处理平时99%的数据排列//开1000个是因为极端情况下越界严重if (st == NULL) {printf("栈空间申请失败\n");return;}int top = 0;//一个栈顶变量和一个数组构成的简易栈st[top++] = right;st[top++] = left;while (top != 0) {int l = st[top - 1];--top;int r = st[top - 1];--top;int keyi = partSortHole(a, l, r);//单趟排序if (keyi + 1 < r) {st[top++] = r;//右边先入栈st[top++] = keyi + 1;}if (l < keyi - 1) {st[top++] = keyi - 1;//左边后入栈st[top++] = l;}}free(st);
}//快排的非递归实现,前后指针
void quickSortNonRDPoint(Datatype* a, int left, int right) {int* st = (int*)malloc(sizeof(int) * 100 * 10//floor(log(right - left + 1) / log(2)) * 3);//栈开100个空间后就能处理平时99%的数据排列//开1000个是因为极端情况下越界严重if (st == NULL) {printf("栈空间申请失败\n");return;}int top = 0;//一个栈顶变量和一个数组构成的简易栈st[top++] = right;st[top++] = left;while (top != 0) {int l = st[top - 1];--top;int r = st[top - 1];--top;int keyi = partSortDPoint(a, l, r);//单趟排序if (keyi + 1 < r) {st[top++] = r;//右边先入栈st[top++] = keyi + 1;}if (l < keyi - 1) {st[top++] = keyi - 1;//左边后入栈st[top++] = l;}}free(st);
}void _mergeSort(Datatype* a, int begin, int end, Datatype* tmp) {if (begin >= end)return;//不可能出现begin>end的情况//除非c语言的除法运算是向上取整 小区间优化,优化效率在10%~20%//if (end - begin + 1 < 10) {//区间选择[8,15]最佳//	insertSort(a + begin, end - begin + 1);//	return;//}int mid = (begin + end) / 2;//将这组数据分成两个区间: [begin, mid] [mid+1, end]_mergeSort(a, begin, mid, tmp);_mergeSort(a, mid + 1, end, tmp);// 归并两个区间int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2) {//合并区间if (a[begin1] < a[begin2])tmp[i++] = a[begin1++];elsetmp[i++] = a[begin2++];}//将剩余的数接在临时数组的尾部//理论上下面这两个while循环只有一个能进行while (begin1 <= end1)tmp[i++] = a[begin1++];while (begin2 <= end2)tmp[i++] = a[begin2++];for (i = begin; i <= end; i++)a[i] = tmp[i];
}//归并排序,无小区间优化
void mergeSort(Datatype* a, int n) {Datatype* tmp = (Datatype*)malloc(sizeof(Datatype) * n);_mergeSort(a, 0, n - 1, tmp);free(tmp);
}void _mergeSortPlus(Datatype* a, int begin, int end, Datatype* tmp) {if (begin >= end)return;//不可能出现begin>end的情况//除非c语言的除法运算是向上取整// 小区间优化,优化效率在10%~20%if (end - begin + 1 < 10) {//区间选择[8,15]最佳insertSort(a + begin, end - begin + 1);return;}int mid = (begin + end) / 2;//将这组数据分成两个区间: [begin, mid] [mid+1, end]_mergeSortPlus(a, begin, mid, tmp);_mergeSortPlus(a, mid + 1, end, tmp);// 归并两个区间int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2) {//合并区间if (a[begin1] < a[begin2])tmp[i++] = a[begin1++];elsetmp[i++] = a[begin2++];}//将剩余的数接在临时数组的尾部//理论上下面这两个while循环只有一个能进行while (begin1 <= end1)tmp[i++] = a[begin1++];while (begin2 <= end2)tmp[i++] = a[begin2++];for (i = begin; i <= end; i++)a[i] = tmp[i];
}//归并排序,小区间优化
void mergeSortPlus(Datatype* a, int n) {Datatype* tmp = (Datatype*)malloc(sizeof(Datatype) * n);_mergeSortPlus(a, 0, n - 1, tmp);free(tmp);
}//归并排序非递归实现
void mergeSortNonR(Datatype* a, int n) {Datatype* tmp = (Datatype*)malloc(sizeof(Datatype) * n);if (tmp == NULL)return;//通过控制每组要合并的数据个数控制排序本体//gap是每组的数据个数,可以是1,2,4,....,n/2int gap = 1;while (gap < n) {int j = 0, i = 0;//每次循环跳过两个区间for (i = 0; i < n; i += 2 * gap) {int begin1 = i, end1 = i + gap - 1;//这样一个区间的数据个数为gap个int begin2 = i + gap, end2 = i + 2 * gap - 1;//int j = i;//若j放在for循环内,则要初始化为i。也就是最左边区间的左边界// 修正奇数分组归并时区间1囊括所有数据的情况if (end1 >= n || begin2 >= n)break;//修正奇数分组归并时区间2的组数不够导致右区间越界的情况if (end2 >= n)end2 = n - 1;//之后就是正常的归并排序while (begin1 <= end1 && begin2 <= end2)if (a[begin1] < a[begin2])tmp[j++] = a[begin1++];elsetmp[j++] = a[begin2++];while (begin1 <= end1)tmp[j++] = a[begin1++];while (begin2 <= end2)tmp[j++] = a[begin2++];// 归并一组,拷贝一组//将所有参与归并的数据进行拷贝// 参与归并的区间为[i,end2]//memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));//这里j的最终的值和end2是一样的,即使经过这一轮拷贝for (j = i; j <= end2; j++)a[j] = tmp[j];}//memcpy(a, tmp, sizeof(int) * n);//有用break阻止后续归并,一次性拷贝一层无法处理越界的情况,gap *= 2;}free(tmp);
}//计数排序
void countSort(Datatype* a, int n) {Datatype min = a[0], max = a[0];int i;for (i = 0; i < n; i++) {if (a[i] < min)min = a[i];if (a[i] > max)max = a[i];}int range = max - min + 1;//求数据的范围Datatype* countA = (Datatype*)malloc(sizeof(Datatype) * range);if (countA == NULL)return;memset(countA, 0, sizeof(Datatype) * range);//计数数组// 统计次数for (i = 0; i < n; i++)countA[a[i] - min]++;// 排序int k = 0, j = 0;for (j = 0; j < range; j++)while (countA[j]--)a[k++] = j + min;free(countA);
}

test.cpp

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS 1
#endif
/**/
#include"sortAlgorithm.h"
#include<chrono>
#include<iostream>
using namespace std;int sortJudg(Datatype* a, int n) {//判断数据是否升序int i = 0;for (i = 0; i < n - 1; i++)if (a[i] > a[i + 1])break;return i == n - 1;
}void f0() {//测试排序算法的可行性srand((size_t)time(0));
#define NUM 100Datatype* a = (Datatype*)malloc(sizeof(Datatype) * NUM),* b = (Datatype*)malloc(sizeof(Datatype) * NUM);if (a == NULL) return;if (b == NULL) return;for (int i = 0; i < NUM; i++) {a[i] = rand() + 1;b[i] = a[i];}//函数指针数组void (*f[13])(Datatype*, int) = {selectSort,selectSortplus,heapSort,heapSortCopy,insertSort,insertSortPlus,shellSort,shellSort2,bubbleSort,mergeSort,mergeSortPlus,mergeSortNonR,countSort};void (*f2[7])(Datatype*, int, int) = {quickSortHoare,quickSortHole,quickSortDPoint,quickSortTRoute,quickSortNonRHoare,quickSortNonRHole,quickSortNonRDPoint};for (int i = 0; i < 13; i++) {f[i](a, NUM);printf("%d\n", sortJudg(a, NUM));for (int i = 0; i < NUM; i++) a[i] = b[i];}for (int i = 0; i < 7; i++) {f2[i](a, 0, NUM - 1);printf("%d\n", sortJudg(a, NUM));for (int i = 0; i < NUM; i++) a[i] = b[i];}free(a);free(b);
#undef NUM
}void _f1(Datatype* a, int n,void (*f)(Datatype*, int), const char* st) {auto begin = std::chrono::high_resolution_clock::now();f(a, n);auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double> time1 = end - begin;std::cout << st << ":" << time1.count() << endl;
}//c++的函数重载
void _f1(Datatype* a, int n,void (*f)(Datatype*, int, int), const char* st) {auto begin = std::chrono::high_resolution_clock::now();f(a, 0, n - 1);auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double> time1 = end - begin;std::cout << st << ":" << time1.count() << endl;
}void f1() {srand((size_t)time(0));int n = 10;int i = 0;while (n <= 100000) {Datatype* a = (Datatype*)malloc(sizeof(Datatype) * n),* b = (Datatype*)malloc(sizeof(Datatype) * n);if (a == NULL)return;if (b == NULL)return;for (i = 0; i < n; i++) {a[i] = (rand()*rand())%n + 1;b[i] = a[i];}printf("%d个数据的情况:\n", n);_f1(a, n, selectSort, "selectSort");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, selectSortplus, "selectSortplus");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, heapSort, "heapSort");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, heapSortCopy, "heapSortCopy");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, insertSort, "insertSort");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, insertSortPlus, "insertSortPlus");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, shellSort, "shellSort");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, shellSort2, "shellSort2");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, bubbleSort, "bubbleSort");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, quickSortHoare, "quickSortHoare");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, quickSortHole, "quickSortHole");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, quickSortDPoint, "quickSortDPoint");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, quickSortTRoute, "quickSortTRoute");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, quickSortNonRHoare, "quickSortNonRHoare");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, quickSortNonRHole, "quickSortNonRHole");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, quickSortNonRDPoint, "quickSortNonRDPoint");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, mergeSort, "mergeSort");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, mergeSortPlus, "mergeSortPlus");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, mergeSortNonR, "mergeSortNonR");for (i = 0; i < n; i++) a[i] = b[i];_f1(a, n, countSort, "countSort");for (i = 0; i < n; i++) a[i] = b[i];free(a);free(b);n *= 10;printf("\n");}
}int main() {//f0();//排序算法可行性分析f1();//测试排序耗时return 0;
}

这里只敢放10万个数据,因为个别时间复杂度为 O ( n 2 ) O(n^2) O(n2)的排序算法实在太慢了。后面会对数据进行微调来测试不用的情况。

时间复杂度测试结果

其中最具有代表性的是10个数据的情况和大量数据的情况。受到设备的影响,结果可能有差异,所以这里只举其中一个样例。这里的浮点数例如1.4e-05表示的是 1.4 × 1 0 − 5 1.4\times10^{-5} 1.4×105单位是秒

10个数据的情况:
selectSort:1.04e-05
selectSortplus:3e-07
heapSort:3e-07
heapSortCopy:1.6e-06
insertSort:4e-07
insertSortPlus:1e-07
shellSort:3e-07
shellSort2:3e-07
bubbleSort:2e-07
quickSortHoare:4e-07
quickSortHole:4e-07
quickSortDPoint:4e-07
quickSortTRoute:4e-07
quickSortNonRHoare:1.8e-06
quickSortNonRHole:1.4e-06
quickSortNonRDPoint:1.7e-06
mergeSort:1.8e-06
mergeSortPlus:1.6e-06
mergeSortNonR:1.4e-06
countSort:7e-07

可以看出,插入排序在这种小数据的情况下性能是最优的,这也是为什么小区间优化会选择插入排序的原因。

接下来是10w个数据的情况。可以看到时间复杂度为 O ( n 2 ) O(n^2) O(n2)的排序已经吃不消了,冒泡排序直接蚌埠住了。

100000个数据的情况:
selectSort:2.45815
selectSortplus:5.77021
heapSort:0.0047019
heapSortCopy:0.0050789
insertSort:1.43777
insertSortPlus:0.87611
shellSort:0.0112118
shellSort2:0.0087459
bubbleSort:12.4946
quickSortHoare:0.0050874
quickSortHole:0.0050904
quickSortDPoint:0.0054573
quickSortTRoute:0.005225
quickSortNonRHoare:0.0053119
quickSortNonRHole:0.004995
quickSortNonRDPoint:0.0051825
mergeSort:0.0092598
mergeSortPlus:0.0087718
mergeSortNonR:0.0075344
countSort:0.0006727

之后将 O ( n 2 ) O(n^2) O(n2)的算法给注释掉,测试极端情况下各个算法排序的性能。

100w是自己的设备在运行带有递归的排序算法时的极限,再扩大10倍就会栈溢出。

1000000个数据的情况:
heapSort:0.407758
heapSortCopy:0.468729
shellSort:0.217771
shellSort2:0.171042
quickSortHoare:0.185388
quickSortHole:0.133911
quickSortDPoint:0.218722
quickSortTRoute:0.251578
quickSortNonRHoare:0.153858
quickSortNonRHole:0.120381
quickSortNonRDPoint:0.204297
mergeSort:0.147725
mergeSortPlus:0.12035
mergeSortNonR:0.134911
countSort:0.0056529

再将所有带有递归的排序算法注释掉。

1000000个数据的情况:
heapSort:0.386531
heapSortCopy:0.408616
shellSort:0.202543
shellSort2:0.167401
quickSortNonRHoare:0.154687
quickSortNonRHole:0.120555
quickSortNonRDPoint:0.221774
mergeSortNonR:0.129261
countSort:0.005713910000000个数据的情况:
heapSort:5.64962
heapSortCopy:5.85666
shellSort:2.93551
shellSort2:2.6005
quickSortNonRHoare:2.75501
quickSortNonRHole:3.13497
quickSortNonRDPoint:3.75697
mergeSortNonR:1.463
countSort:0.0653788100000000个数据的情况:
heapSort:89.2091
heapSortCopy:89.406
shellSort:41.1887
shellSort2:27.0528
quickSortNonRHoare:146.442
quickSortNonRHole:172.822
quickSortNonRDPoint:179.666
mergeSortNonR:15.9719
countSort:0.66968

快排的特点是重复越多,效率越慢,而rand函数返回的最大值为32767,于是将数组初始化为rand()*rand()+1以减少重复后的测试结果:

1000000个数据的情况:
heapSort:0.360787
heapSortCopy:0.368785
shellSort:0.240912
shellSort2:0.169701
quickSortNonRHoare:0.158003
quickSortNonRHole:0.116451
quickSortNonRDPoint:0.241888
mergeSortNonR:0.139041
countSort:0.001281810000000个数据的情况:
heapSort:6.07863
heapSortCopy:6.02169
shellSort:3.52872
shellSort2:2.82663
quickSortNonRHoare:1.79774
quickSortNonRHole:1.33191
quickSortNonRDPoint:2.63835
mergeSortNonR:1.62083
countSort:0.0128354100000000个数据的情况:
heapSort:89.2351
heapSortCopy:95.0854
shellSort:51.2792
shellSort2:36.6936
quickSortNonRHoare:20.3174
quickSortNonRHole:15.1538
quickSortNonRDPoint:30.2327
mergeSortNonR:18.416
countSort:0.130009

到这个应该用外排序来操作的数据量,这些算法能兼职足够说明它们都很优秀。我们能做的是根据平时的情况选择合适的排序算法来进行数据排序。

相关文章:

迄今为止的排序算法总结

迄今为止的排序算法总结 7.10 迄今为止的排序算法总结复杂度和稳定性时间复杂度测试程序sortAlgorithm.hsortAlgorithm.cpptest.cpp 时间复杂度测试结果 7.10 迄今为止的排序算法总结 复杂度和稳定性 排序算法平均情况最好情况最坏情况稳定性空间复杂度选择排序O(n^2)O(n^2)O…...

HTML和CSS 表单、表格练习

HTML和CSS 表格练习 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>HTML表格练习</title>…...

H5流媒体播放器EasyPlayer.js网页直播/点播播放器如果H.265视频在播放器上播放不流畅,可以考虑的解决方案

随着流媒体技术的迅速发展&#xff0c;H5流媒体播放器已成为现代网络视频播放的重要工具。其中&#xff0c;EasyPlayer.js网页直播/点播播放器作为一款功能强大的H5播放器&#xff0c;凭借其全面的协议支持、多种解码方式以及跨平台兼容性&#xff0c;赢得了广泛的关注和应用。…...

Http 转 https 中 Nginx 的详细配置过程

摘要 本节将简要介绍从 HTTP 到 HTTPS 的配置过程&#xff0c;并完整展示 Nginx 的相关配置信息。 经过两天断断续续的调试&#xff0c;终于将 http 变成 https 了。现在说说这个安装 ssl 证书的过程。 服务器是在某云上。这个过程大致分为三个步骤&#xff1a;申请 ssl 证书、…...

【测试工具JMeter篇】JMeter性能测试入门级教程(二)出炉,测试君请各位收藏了!!!

上篇文章&#xff1a;CSDN 我们介绍了JMeter的一些原理介绍&#xff0c;以及安装配置和启动流程&#xff0c;本文我们就来讲讲JMeter如何使用。 一、JMeter目录结构组成 1. 根目录 Jmeter安装包解压后的根目录如下图&#xff1a; 1.1 backups目录&#xff1a;脚本备份目录&am…...

Otter 安装流程

优质博文&#xff1a;IT-BLOG-CN 一、背景 随着公司的发展&#xff0c;订单库的数据目前已达到千万级别&#xff0c;需要进行分表分库&#xff0c;就需要对数据进行迁移&#xff0c;我们使用了otter&#xff0c;这里简单整理下&#xff0c;otter 的安装过程&#xff0c;希望对…...

一文学会Golang里拼接字符串的6种方式(性能对比)

g o l a n g golang golang的 s t r i n g string string类型是不可修改的&#xff0c;对于拼接字符串来说&#xff0c;本质上还是创建一个新的对象将数据放进去。主要有以下几种拼接方式 拼接方式介绍 1.使用 s t r i n g string string自带的运算符 ans ans s2. 使用…...

【笔记】Linux下编译Python3.10.15为动态库同时正确处理OpenSSL3依赖

之前自己第一次编译Python后发现pip会提示无法使用SSL&#xff0c;后来了解到是自己编译时没有配置OpenSSL。这个过程有点曲折&#xff0c;里面有一个坑&#xff0c;怕忘记于是写博客记录一下。 首先是下载OpenSSL&#xff0c;Python3.10.15支持此时最新版的OpenSSL 3.4.0&…...

Go语言获取客户端真实IP

在一些需求中&#xff0c;服务器需要记录客户端的ip地址&#xff0c;要获取ip地址&#xff0c;则需要有http.Request的对象参数传入&#xff0c;以下代码直接放在util中使用。 文件名&#xff1a;ip_utils.go package utilsimport ("context""github.com/spf1…...

大模型论文速递(11.23-11.25)

BlueLM-V3B 关键词&#xff1a;动态分辨率&#xff0c;图像放大&#xff0c;适应性网格化方法 研究问题&#xff1a;如何改进现有的动态分辨率匹配方法以减少在模型训练和部署中的计算复杂度&#xff1f; 方法&#xff1a; 分析现有动态分辨率匹配算法&#xff08;如LLaVA-…...

维护在线重做日志(二)

迁移和重命名 可以使用操作系统命令重新定位重做日志&#xff0c;然后使用ALTER DATABASE语句使数据库知道它们的新名称&#xff08;位置&#xff09;。这个过程是必要的&#xff0c;例如&#xff0c;如果当前用于一些重做日志文件的磁盘将被删除&#xff0c;或者如果数据文件…...

.net core MVC入门(一)

文章目录 项目地址一、环境配置1.1 安装EF core需要包1.2 配置数据库连接二、使用EF创建表2.1 整体流程梳理2.1 建表详细流程三、添加第一个视图3.1整体流程梳理3.1 添加视图,并显示在web里四、使用EF增加Catogory数据,并且读取数据到页面4.1整体流程梳理4.2 实现五、增加Cat…...

802.11协议

802.11协议是由美国电气和电子工程师协会&#xff08;IEEE&#xff09;制定的无线局域网&#xff08;WLAN&#xff09;标准。以下是关于802.11协议的详细介绍&#xff1a; 一、定义与背景 定义&#xff1a;IEEE802.11是美国电机电子工程师协会&#xff08;IEEE&#xff09;为…...

【Linux】线程ID与互斥、同步(锁、条件变量)

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux 创作时间 &#xff1a;2024年11月24日 线程ID及进程地址空间布局 先看一下这段代码&#xff1a; 运行一下&#xff1a; 运行这个代码之后&#xff0c;我们看到的这个很大的数字就是线程id&#xff0c;然后…...

Android 13 编译Android Studio版本的Launcher3

Android 13 Aosp源码 源码版本 Android Studio版本 Launcher3QuickStepLib (主要代码) Launcher3ResLib(主要资源) Launcher3IconLoaderLib(图片加载&#xff0c;冲突资源单独新建) 需要值得注意的是&#xff1a; SystemUISharedLib.jar 有kotlin和java下的&#xff0c;在 Lau…...

burp功能介绍

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…...

Android12 的 Vold梳理

1.代码位置 system/vold/ 路径下,查看bp文件&#xff0c;发现是编译system/vold/main.cpp编译生成可执行文件vold 2.app侧调用代码流程 2.1 整体框架 #mermaid-svg-lqO8phN62rKNW407 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#…...

[pdf,epub]162页《分析模式》漫谈合集01-35提供下载

《分析模式》漫谈合集01-35的pdf、epub文件&#xff0c;已上传至本号的CSDN资源。 如果CSDN资源下载有问题&#xff0c;可到umlchina.com/url/ap.html。 已排版成适合手机阅读&#xff0c;pdf的排版更好一些。 ★UMLChina为什么叒要翻译《分析模式》&#xff1f; ★[缝合故事…...

Vue2教程003:Vue指令之v-bind和v-for

文章目录 2.6 v-bind2.7 图片轮播案例2.8 v-for2.9 图书管理案例 2.6 v-bind 作用&#xff1a;动态设置html的标签属性->src、url、title…语法&#xff1a;v-bind:属性名"表达式" 动态设置img标签的src属性&#xff1a; <body> <div id"app&quo…...

Pathlib操作文件IN Python

系列文章目录 文章目录 目录 系列文章目录 文章目录 前言 一、Pathlib是什么&#xff1f; 二、使用步骤 前言 pathlib 是 Python 标准库中用于操作文件和目录路径的模块&#xff0c;自 Python 3.4 起引入。它提供了一种面向对象的方式处理路径&#xff0c;使路径操作更加简洁、…...

AOC显示器915Sw按键失灵维修记

大家好&#xff0c;我是 程序员码递夫 今天给大家分享的是自己维修老古董AOC液晶显示器按键失灵的的过程&#xff0c;实属DIY记录。 1、引子 家里有台老古董的19寸AOC液晶显示器&#xff08;型号915Sw&#xff09;, 一直作为我的副显示器陪伴着左右&#xff0c;显示还正常&a…...

霍曼转移方法介绍

霍曼转移方法介绍 背景 在航天工程中&#xff0c;轨道转移是指航天器从一个轨道移动到另一个轨道的过程。为了高效利用燃料并缩短转移时间&#xff0c;科学家们开发了多种轨道转移方法。其中&#xff0c;霍曼转移&#xff08;Hohmann Transfer&#xff09;因其燃料效率高、计…...

我的创作之路:机缘、收获、日常与未来的憧憬

目录 前言机缘收获 日常成就一个优化后的二分查找实现 憧憬 前言 每个人的成长旅程都有它独特的轨迹&#xff0c;而我的这段技术创作之路&#xff0c;则源于一次再普通不过的项目分享。 机缘 一切的开始其实是偶然。在一次项目中&#xff0c;我遇到了一个棘手的问题&#xf…...

《硬件架构的艺术》笔记(六):处理字节顺序

介绍 本章主要介绍字节顺序的的基本规则。&#xff08;感觉偏软件了&#xff0c;不知道为啥那么会放进《硬件架构的艺术》这本书&#xff09;。 定义 字节顺序定义数据在计算机系统中的存储格式&#xff0c;描述存储器中的MSB和LSB的位置。对于数据始终以32位形式保存在存储器…...

AddIPAddress添加临时IP后,socket bind失败

问题描述 在Win10\Win11下使用addIPAddress添加临时IP成功后&#xff0c;立即创建socket&#xff0c;bind失败 if(!m_socket->bind(QHostAddress(m_localIP), listenPort)) {qCritical() << QString("bind error %1").arg(m_socket->errorString());re…...

关于IDE的相关知识之一【使用技巧】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于ide使用技巧的相关内容&#xff01; 关于…...

线性代数在人工智能领域中的实践

一、机器学习中的线性代数应用 在机器学习中&#xff0c;线性代数主要用于构建和训练各种模型&#xff0c;如线性回归、逻辑回归、支持向量机等。这些模型在数据的特征提取、降维处理以及分类等方面发挥了重要作用。 线性回归&#xff1a;线性回归是最简单的机器学习算法之一…...

图片生成视频-右进

右侧进入 ffmpeg -loop 1 -i image.jpg -f lavfi -i colorcblack:s1280x720:d20 -filter_complex "[1:v]formatrgba[bg];[0:v]formatrgba,scale1280:720[img];[bg][img]overlayxif(lt(t,3),W,if(lt(t,8),W-(t-3)*W/5,0)):y(H-h)/2:enablegte(t,3)" -c:v libx264 -t 2…...

3、集线器、交换机、路由器、ip的关系。

集线器、交换机、路由器三者的关系 1、集线器2、交换机&#xff08;每个交换机是不同的广播域&#xff0c;ip地址起到划分广播域的作用&#xff09;3、 路由器4、ip地址 所有图片和资料均来源于B站&#xff1a;网络安全收藏家 1、集线器 一开始两台电脑通信就需要网线就可以&a…...

w~视觉~合集25

我自己的原文哦~ https://blog.51cto.com/whaosoft/12627822 #Mean Shift 简单的介绍 Mean Shift 的数学原理和代码实现,基于均值漂移法 Mean Shift 的图像分割 Mean Shift 算法简介 从分割到聚类 对于图像分割算法&#xff0c;一个视角就是将图像中的某些点集分为一类&a…...