zYzYzYzYz

多种排序算法的效率观察

zYzYzYzYz·2024-04-28 23:49·30 次阅读

多种排序算法的效率观察

注:时间的单位为毫秒,每个数据均观测三次取平均值(排除异常数据)。

时间复杂度较大的排序算法#

随机数据耗时#

数据规模 选择排序 冒泡排序 插入排序 猴子排序
10 0 0 0 178
100 0 0 0 -
103 0 0 0 -
104 130 124 24 -

分析#

选择排序、冒泡排序、插入排序时间复杂度均为 O(n2)​,但是插入排序的时间却比其余两者小。原因是如果插入的数字不太极端的话,每一个数插到中间就break了,上限跑不满。

顺序数据耗时#

数据规模 选择排序 冒泡排序 插入排序
104 35 0 0

由于冒泡排序和插入排序当检测到序列已经升序时,可以直接停止算法,使得二者在第一次循环结束时就结束了。选择排序仅仅是比较了 n×(n1)2 次大小,省去了交换两数的过程,时间得到较大减少。

逆序数据耗时#

数据规模 选择排序 冒泡排序 插入排序
104 40 79 52

选择排序竟然反杀了冒泡和插入。也许 c++ 会这点代码给优化了,于是用C语言重新写了一遍,发现有:

数据规模 选择排序 冒泡排序 插入排序
104 163 165 96

这符合之前的规律。但是为什么逆序时c++会出现反常还有待讨论。

时间复杂度较小的排序算法#

耗时#

数据规模 归并排序 堆排序 快速排序 快速排序(std)
103 0 0 0 0
104 0 1 0 0
105 12 12 8 5
106 136 176 121 84
107 1703 2926 1397 904

这些算法复杂度均为 O(nlogn) ,相对于之前的 O(n2) 算法快了不少。手写堆排序常数较大,用stl的priority_queue可以优化一下常数;stl的快速排序效率非常之高,其中一个优化是当数据范围较小的时候直接进行插入排序,而非递归进行快速排序。

顺序数据耗时#

数据规模 归并排序 堆排序 快速排序 快速排序(std)
105 4 10 3 1
106 53 116 40 12
107 634 1297 491 176

逆序数据耗时#

数据规模 归并排序 堆排序 快速排序 快速排序(std)
105 4 7 3 1
106 47 98 40 10
107 583 1048 501 129

我们可以发现,当数据有序时排序的速度比乱序时快。但是逆序时部分算法比顺序时快一点(目前并没有搞明白是为什么)。

复杂度与值域相关的排序算法#

耗时#

数据规模 值域 桶排序 基数排序
104 104 0 0
104 107/109 54 0
105 104 0 2
105 107/109 57 4
106 104 2 19
106 107/109 86 41
107 104 23 254
107 107/109 344 483

由于开 109 个变量需要一个超大的内存,故桶排序的较大值域设为 107

相关代码#

选择排序#

Copy
#include<bits/stdc++.h> using namespace std; int n; int a[100010]; mt19937 mt(123456); void xzsort() { for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(a[i]>a[j]) swap(a[i],a[j]); } int main() { n=10000; for(int i=1;i<=n;++i) a[i]=mt()%1000000000ll; // sort(a+1,a+1+n); // reverse(a+1,a+1+n); int abc=clock(); xzsort(); cout<<clock()-abc<<'\n'; }

冒泡排序#

Copy
#include<bits/stdc++.h> #define LL long long using namespace std; int n; int a[10010]; mt19937 mt(123456); void mpsort() { for(int i=1;i<=n;i++){ bool flag=0; for(int j=1;j<=n-i;j++) if(a[j]>a[j+1]) swap(a[j+1],a[j]),flag=1; if(!flag)break; } } int main() { n=10000; for(int i=1;i<=n;++i) a[i]=mt()%1000000000ll; sort(a+1,a+1+n); reverse(a+1,a+1+n); int abc=clock(); mpsort(); cout<<clock()-abc<<'\n'; }

插入排序#

Copy
#include<bits/stdc++.h> #define LL long long using namespace std; int n; LL a[10010],b[10010]; mt19937 mt(123456); void mpsort() { for(int i=1;i<=n;i++){ b[i]=a[i]; for(int j=i;j>1;j--) if(a[j]<a[j-1]) swap(a[j],a[j-1]); else break; } memcpy(a,b,sizeof(LL)*(n+3)); } int main() { n=10000; for(int i=1;i<=n;++i) a[i]=mt()%1000000000ll; sort(a+1,a+1+n); reverse(a+1,a+1+n); int abc=clock(); mpsort(); cout<<clock()-abc<<'\n'; }

归并排序#

Copy
#include<bits/stdc++.h> #define LL long long using namespace std; int n; LL a[10000010],b[10000010]; mt19937 mt(123456); void mergesort(int L,int R) { if(L==R)return; int M=(L+R)>>1; mergesort(L,M); mergesort(M+1,R); int Lp=L,Rp=M+1,pos=L; while(Lp<=M||Rp<=R){ if((a[Lp]<=a[Rp]&&Lp<=M)||Rp>R) b[pos++]=a[Lp++]; else b[pos++]=a[Rp++]; } memcpy(a+L,b+L,sizeof(LL)*(R-L+1)); } int main() { n=10000000; for(int i=1;i<=n;++i) a[i]=mt()%1000000000ll; sort(a+1,a+1+n); reverse(a+1,a+1+n); int abc=clock(); mergesort(1,n); cout<<clock()-abc<<'\n'; }

堆排序#

Copy
#include<bits/stdc++.h> #define LL long long using namespace std; int n; LL a[10000010],DUI[10000010],NUM; mt19937 mt(123456); void UP(int x) { while(x>1){ if(DUI[x]<=DUI[x>>1])return; DUI[x]^=DUI[x>>1]^=DUI[x]^=DUI[x>>1]; x>>=1; } } void DOWN(int x) { int y=x<<1; while(y<=NUM){ if(y<NUM&&DUI[y+1]>DUI[y]) y++; if(DUI[x]>=DUI[y])return; DUI[x]^=DUI[y]^=DUI[x]^=DUI[y]; x=y;y<<=1; } } void ADD(LL x) { DUI[++NUM]=x; UP(NUM); } void DEL() { DUI[1]=DUI[NUM--]; DOWN(1); } void duisort(LL A[],int Len) { NUM=0; for(int i=1;i<=Len;i++) ADD(A[i]); for(int i=Len;i;i--) A[i]=DUI[1],DEL(); } int main() { n=10000000; for(int i=1;i<=n;++i) a[i]=mt()%1000000000ll; sort(a+1,a+1+n); reverse(a+1,a+1+n); int abc=clock(); duisort(a,n); cout<<clock()-abc<<'\n'; }

快速排序#

Copy
#include<bits/stdc++.h> #define LL long long using namespace std; const int bit[4]={0,16,32,48},U=65535; LL n,a[10000010],Maxa[10000010],Mina[10000010]; int st[66000]; mt19937 mt(123456); void qsort(int L,int R) { if(L>=R)return; int M=(L+R)>>1,Lp=0,Rp=0; LL Max=max(a[M],max(a[L],a[R])),Min=min(a[M],min(a[L],a[R])); LL temp=a[M]^a[L]^a[R]^Max^Min; for(int i=L;i<=R;i++) if(a[i]<temp)Mina[++Lp]=a[i]; else if(a[i]>temp)Maxa[++Rp]=a[i]; memcpy(a+L,Mina+1,sizeof(LL)*Lp); fill(a+L+Lp,a+R-Rp,temp); memcpy(a+R-Rp+1,Maxa+1,sizeof(LL)*Rp); qsort(L,L+Lp-1); qsort(R-Rp+1,R); } int main() { n=10000000; for(int i=1;i<=n;++i) a[i]=mt()%1000000000ll; sort(a+1,a+1+n); reverse(a+1,a+1+n); int abc=clock(); qsort(1,n); cout<<clock()-abc<<'\n'; }

快速排序std#

Copy
#include<bits/stdc++.h> #define LL long long using namespace std; int n; LL a[10000010]; mt19937 mt(123456); int main() { n=10000000; for(int i=1;i<=n;++i) a[i]=mt()%1000000000ll; sort(a+1,a+1+n); reverse(a+1,a+1+n); int abc=clock(); sort(a+1,a+1+n); cout<<clock()-abc<<'\n'; }

基数排序#

Copy
#include<bits/stdc++.h> #define LL long long using namespace std; const int bit[4]={0,16,32,48},U=65535; LL n,a[10000010],b[10000010]; int st[66000]; mt19937 mt(123456); void radixsort() { for(int d=0;d<2;++d){ memset(st,0,sizeof st); for(int i=1;i<=n;++st[(a[i++]>>bit[d])&U]); for(int i=1;i<=U;++i)st[i]+=st[i-1]; for(int i=n;i>0;--i)b[st[(a[i]>>bit[d])&U]--]=a[i]; memcpy(a,b,sizeof(LL)*(n+5)); } } int main() { n=1000000; for(int i=1;i<=n;++i) a[i]=mt()%10000ll; int abc=clock(); radixsort(); cout<<clock()-abc<<'\n'; }

桶排序#

Copy
#include<bits/stdc++.h> #define LL long long using namespace std; LL n,a[10000010],c[10000010]; LL b[10000010]; int st[66000]; mt19937 mt(123456); void tongsort() { for(int i=1;i<=n;++b[a[i++]]); for(int i=n=0;i<=10000;i++) while(b[i]--)a[++n]=i; } int main() { n=100000; for(int i=1;i<=n;++i) a[i]=mt()%10000; int abc=clock(); tongsort(); cout<<clock()-abc<<'\n'; }
posted @   zYzYzYzYz  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示
目录