迄今为止的排序算法总结

迄今为止的排序算法总结

  • 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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/61848.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot集成ES(ElasticSearch)

1.导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>导入依赖后&#xff0c;注意在依赖中查看对应的版本是否与本机ES对应 2.创建配置并…

数据结构之二:表

顺序表代码&#xff1a;SData/SqList/SeqList.h Hera_Yc/bit_C_学习 - 码云 - 开源中国 链表相关代码&#xff1a;SData/ListLink/main.c Hera_Yc/bit_C_学习 - 码云 - 开源中国 本文主要讲解的是线性表&#xff08;逻辑线性&#xff09;&#xff0c;对于非线性表不做补充。…

《Python基础》之循环结构

目录 简介 一、for循环 1、基本语法与作用 2、使用 range() 函数配合 for 循环 3、嵌套的for循环 二、while循环 1、基本语法与作用 2、while 循环嵌套 &#xff08;1&#xff09;、while循环与while循环嵌套 &#xff08;2&#xff09;、while循环与for循环嵌套 简介 …

基于LiteFlow的风控系统指标版本控制

个人博客&#xff1a;无奈何杨&#xff08;wnhyang&#xff09; 个人语雀&#xff1a;wnhyang 共享语雀&#xff1a;在线知识共享 Github&#xff1a;wnhyang - Overview 更新日志 最近关于https://github.com/wnhyang/coolGuard此项目更新了如下内容&#xff1a;https://g…

Mysql中的 TEXT 和 BLOB 解析

&#x1f680; 博主介绍&#xff1a;大家好&#xff0c;我是无休居士&#xff01;一枚任职于一线Top3互联网大厂的Java开发工程师&#xff01; &#x1f680; &#x1f31f; 在这里&#xff0c;你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人&#xff0c;我不仅热衷…

241124_文本解码原理

241124_文本解码原理 一个文本序列的概率分布可以分解为每个词基于其上文的条件概率的乘积。 Greedy search 就是每步都选择概率最大的&#xff0c;不会去考虑全局 按照贪心搜索输出the nice woman 的概率就是0.5*0.40.2 这种方法简单&#xff0c;但也存在问题&#xff0c;比…

介绍一下strlwr(arr);(c基础)

hi , I am 36 适合对象c语言初学者 strlwr(arr)&#xff1b;函数是把arr数组变为小写字母 格式 #include<string.h> strlwr(arr); 返回值为arr 链接分享一下arr的意义(c基础)(必看)(牢记)-CSDN博客 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #incl…

16:(标准库)ADC三:使用外部触发启动ADC/模拟看门狗

使用外部触发启动ADC 1、外部中断线EXTI11触发ADC2、外部定时器TIM2_CH2触发ADC3、ADC中模拟看门狗的使用 1、外部中断线EXTI11触发ADC ADC的触发方式有很多&#xff0c;一般情况都是使用软件触发反式启动ADC转换。除了软件触发方式还能使用外部事件触发启动ADC转换。如下图所…

Linux之管道,system V的共享内存,消息队列和信号量

Linux之管道&#xff0c;systemV共享内存和信号量 一.进程间通信1.1进程间通信的目的1.2进程间通信的方式 二.管道2.1管道的概念2.2匿名管道2.3命名管道 三.system V3.1共享内存3.2消息队列3.3信号量 一.进程间通信 在我们之前有关Linux指令的学习时我们使用过“|”这个命令&a…

使用ChatGPT生成和优化电子商务用户需求规格说明书

在电子商务项目开发中&#xff0c;用户需求规格说明书&#xff08;User Requirement Specification, URS&#xff09;是团队沟通与项目成功的基石。然而&#xff0c;面对复杂多变的需求&#xff0c;如何快速生成清晰、完整且具备说服力的文档&#xff1f;这正是AI工具的用武之地…

1+X应急响应(网络)常见网络攻击-SQL注入:

常见网络攻击-SQL注入&#xff1a; SQL注入概述&#xff1a; 动态网站的工作流程&#xff1a; SQL注入的起源&#xff1a; SQL典型的攻击手段&#xff1a; SQL注入的危害&#xff1a; SQL注入的函数&#xff1a; SQL注入类型&#xff1a; 提交方式分类&#xff1a; Get注入&am…

Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图

用户打开 PDF 文档时&#xff0c;他们会看到 PDF 的初始视图。默认情况下&#xff0c;打开 PDF 时不会显示书签面板或缩略图面板。在本文中&#xff0c;我们将演示如何设置文档属性&#xff0c;以便每次启动文件时都会打开书签面板或缩略图面板。 Spire.PDF for .NET 是一款独…

[Docker-显示所有容器IP] 显示docker-compose.yml中所有容器IP的方法

本文由Markdown语法编辑器编辑完成。 1. 需求背景: 最近在启动一个服务时&#xff0c;突然发现它的一个接口&#xff0c;被另一个服务ip频繁的请求。 按理说&#xff0c;之前设置的是&#xff0c;每隔1分钟请求一次接口。但从日志来看&#xff0c;则是1秒钟请求一次&#xff…

单片机GPIO的8种工作模式

1、输入 GPIO_MODE_AIN:模拟输入 GPIO_MODE_IN_FLOATING:浮空输入 GPIO_MODE_IPD:下拉输入 GPIO_MODE_IPU:上拉输入 2、输出 GPIO_MODE_OUT_OD:开漏输出&#xff08;特殊情况使用&#xff09; GPIO_MODE_OUT_PP&#xff1a;推挽输出-----点灯&#xff08;通用&#…

Azkaban部署

首先我们需要现在相关的组件&#xff0c;在这里已经给大家准备好了相关的安装包&#xff0c;有需要的可以自行下载。 只需要启动hadoop集群就可以&#xff0c;如果现在你的hive是打开的&#xff0c;那么请你关闭&#xff01;&#xff01;&#xff01; 如果不关会造成证书冲突…

时钟使能、

时钟使能 如果正确使用&#xff0c;时钟使能能够显著地降低系统功耗&#xff0c;同时对面积或性能的影响极小。但是如果不正确地使用时钟使能&#xff0c; 可能会造成下列后果&#xff1a; • 面积增大 • 密度减小 • 功耗上升 • 性能下降 在许多使用大量控制集的…

视觉经典神经网络与复现:深入解析与实践指南

目录 引言 经典视觉神经网络模型详解 1. LeNet-5&#xff1a;卷积神经网络的先驱 LeNet-5的关键特点&#xff1a; 2. AlexNet&#xff1a;深度学习的突破 AlexNet的关键特点&#xff1a; 3. VGGNet&#xff1a;深度与简洁的平衡 VGGNet的关键特点&#xff1a; 4. ResNe…

【CSS in Depth 2 精译_060】9.3 详解 CSS 作用域的相关概念、最新 @scope 规则的应用及注意事项

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 【第九章 CSS 的模块化与作用域】 ✔️ 9.1 模块的定义 9.1.1 模块和全局样式9.1.2 一个简单的 CSS 模块9.1.3 模块的变体9.1.4 多元素模块 9.2 将模块组合为更大的结构 9.2.1 模块中多个职责的拆分…

uniapp实现开发遇到过的问题(持续更新中....)

1. 在ios模拟器上会出现底部留白的情况 解决方案&#xff1a; 在manifest.json文件&#xff0c;找到开源码视图配置&#xff0c;添加如下&#xff1a; "app-plus" : {"safearea":{"bottom":{"offset" : "none" // 底部安…

React(六)——Redux

文章目录 项目地址基本理解一、配置Redux store二、创建slice配置到store里并使用三、给Slice配置reducers&#xff0c;用来修改初始值 项目地址 教程作者&#xff1a;教程地址&#xff1a; 代码仓库地址&#xff1a; 所用到的框架和插件&#xff1a; dbt airflow基本理解 s…