排序算法大合集
排序算法大合集
翻了翻很久以前写的算法报告,现在整理一下。
由难度从简单到难排序。
桶排序、冒泡排序、选择排序、快速排序。
最简单粗暴——桶排序
(一)桶排序原理
桶排序,是一个目前速度最快的一种排序 基本思想是将无序列数依次装进一个按元素名命名数组中 最后挨个判断每个“桶”中有没有数 有的话将其输出的一种快速排序 优点:速度最快 缺点:只限用于小数据排序,通常会造成巨大的空间损失 适用于小规模且有一定数据范围的数据排序
(二)复杂度分析
时间复杂度:O(n) 空间复杂度:依数据范围而定,一般较大
(三)代码实现
#include<iostream> #include<cstdio> using namespace std; int n;//有n个数要进行排序 int maxn=-1;//设定一个代表着输入数据最大的变量,初始值设为-1 int minn=100010; //设定一个代表着输入数据最小的变量,初始值设为100010 int a[100005];//假设数据大小不超过十万 int main(){ cin>>n; for(int i=1;i<=n;i++) { int k;//定义一个临时变量 cin>>k;//输入 a[k]++;//桶排序重要的一点,将每个元素都装在相应的桶中 if(minn>k)//打擂台 { minn=k; } if(maxn<k)//打擂台 { maxn=k; } } for(int i=minn;i<=maxn;i++)//从数据的最小至大上限依次判断 { while(a[i]!=0)//如果该桶中还存有数据,就将它输出,并将该桶中元素的数量-1,直至数量为0 { cout<<i<<" ";//输出 a[i]--;//数量-1 } } cout<<endl;//随意换行一下 return 0; }
(四)模拟过程
假设我们要为15个数进行排列: 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5
输入第一个1时,a[1]从0开始+1,结果为1
a[1]=1
输入第一个2时,a[2]从0开始+1,结果为1
a[2]=1
输入第一个3时,a[3]从0开始+1,结果为1
a[3]=1
输入第一个4时,a[4]从0开始+1,结果为1
a[4]=1
输入第一个5时,a[5]从0开始+1,结果为1
a[5]=1
输入第一个6时,a[6]从0开始+1,结果为1
a[6]=1
输入第一个7时,a[7]从0开始+1,结果为1
a[7]=1
输入第一个8时,a[8]从0开始+1,结果为1
a[8]=1
输入第一个9时,a[9]从0开始+1,结果为1
a[9]=1
输入第一个10时,a[10]从0开始+1,结果为1
a[10]=1
输入第二个1时,a[1]从1开始+1,结果为2
a[1]=2
输入第二个2时,a[2]从1开始+1,结果为2
a[2]=2
输入第二个3时,a[3]从1开始+1,结果为2
a[3]=2
输入第二个4时,a[4]从1开始+1,结果为2
a[4]=2
输入第一个5时,a[5]从1开始+1,结果为2
a[5]=2
最后,按所有存在数据的数组的顺序排序,输出,即可得到一串有序的数据,即:
1 1 2 2 3 3 4 4 5 5 6 7 8 9 10
(五)总结
桶排序基本上是一种“不用动脑筋”的排序,直接将数据存储在数组内,最后输出的一个过程,虽时间复杂度最快,但往往会浪费巨大的空间复杂度,仅仅适用于小规模的数据排序。
有趣的排序——冒泡排序
(一)冒泡排序原理
重复访问未排序的元素序列,依次审查相应两个元素,若顺序错误,则将其交换,最后得到一个已排序的序列 优点:原理简单,易于理解 缺点:效率低 适用于小规模数据排序
(二)复杂度分析
时间复杂度:O(n*n) 空间复杂度:O(1)
(三)代码实现
#include<iostream> #include<cstdio> using namespace std; int n;//定义一个变量,设有n个数 int a[100005];//假设数量不超过10的5次方 int main(){ cin>>n;//输入 for(int i=1;i<=n;i++)//输入数据 { cin>>a[i]; } for(int i=1;i<n;i++)//从第一个数据开始,进行冒泡排序 { for(int j=1;j<=n-i;j++) { if(a[j]>a[j+1])//如果位置错误,交换 { swap(a[j],a[j+1]); } } } for(int i=1;i<=n;i++)//输出 { cout<<a[i]<<" "; } cout<<endl; return 0; }
(四)模拟过程
由于时间复杂度是O(n*n),数据就不得不小一点: 设有5个数需要排序,分别是 1 5 4 2 3 过程如下:
第一步:
1 5 4 2 3
判断1和5,1<5,不变化
结果:1 5 4 2 3
第二步:1 5 4 2 3
判断5和4,5>4,交换位置
结果:1 4 5 2 3
第三步:
1 4 5 2 3
判断5和2,5>2,交换位置
结果:1 4 2 5 3
第四步:
1 4 2 5 3
判断5和3,5>3,交换位置
结果:1 4 2 3 5
第五步:
1 4 2 3 5
判断1和4,1<4,不变化
结果:1 4 2 3 5
第六步:
1 4 2 3 5
判断4和2,4>2,交换位置
结果:1 2 4 3 5
第七步:
1 2 4 3 5
判断4和3,4>3,交换位置
结果:1 2 3 4 5
第八步:
1 2 3 4 5
判断4和5,4<5,不变化
结果:1 2 3 4 5
第九步:
1 2 3 4 5
判断3和4,3<4,不变化
结果:1 2 3 4 5
第十步:
1 2 3 4 5
判断4和5,4<5,不变化
结果:1 2 3 4 5
至此,全部排序完毕,得到以下有序序列:
1 2 3 4 5
真题运用
[NOIP2006 普及组] 明明的随机数
本题就是考察我们对排序算法的掌握情况,可以将快速排序代码稍加修改后解决,如下:
#include<iostream> #include<cstdio> using namespace std; int n,ans;//定义一个变量,设有n个数,答案为ans int a[105];//本题只需要100即可 int main(){ cin>>n;//输入 for(int i=1;i<=n;i++)//输入数据 { cin>>a[i]; } for(int i=1;i<n;i++)//从第一个数据开始,进行冒泡排序 { for(int j=1;j<=n-i;j++) { if(a[j]>a[j+1])//如果位置错误,交换 { swap(a[j],a[j+1]); } } } for(int i=1;i<=n;i++) { if(a[i]!=a[i-1])//跟之前桶排序一样,也是因为已排过序,直接前后判断 { ans++; } } cout<<ans<<endl;//输出去过重的数量 for(int i=1;i<=n;i++)//输出 { if(a[i]!=a[i-1]) { cout<<a[i]<<" "; } } cout<<endl; return 0; }
也是可以AC的
(五)总结
冒泡排序主要就是通过判断自己和自己的“邻居”的大小来排序,虽简单易懂,但时间复杂度过高,效率过低,不如快速排序。
冒泡排序的亲戚——选择排序
(一)选择排序原理
运用线性查找找到最小值,乃至最大值,即可排好序 优点:原理简单 缺点:效率较低 适用于小规模数据排序
(二)复杂度分析
时间复杂度:O(n*n) 空间复杂度:O(1)
(三)代码实现
#include<iostream> #include<cstdio> using namespace std; int n; int a[1100]; int main(){ cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } 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]); } } } for(int i=1;i<=n;i++) { cout<<a[i]<<" "; } cout<<endl; return 0; }
(四)模拟过程
假设输入的n为5,即有5个数: 5 4 9 8 7
第一:
比较 (5 4)9 8 7
5>4,所以交换5和4
结果:4 5 9 8 7
第二:
比较 (4) 5 (9) 8 7
4<9,无需交换
结果:4 5 9 8 7
第三:
比较 (4)5 9 (8)7
4<8,无需交换
结果:4 5 9 8 7
第四:
比较 (4)5 9 8 (7)
4<7,无需交换
结果:4 5 9 8 7
自此,最小值4已被确定,下一轮
第一:
比较 4 (5) (9) 8 7
5<9,无需交换
结果:4 5 9 8 7
第二:
比较 4 (5) 9 (8) 7
5<8,无需交换
结果:4 5 9 8 7
第三:
比较 4 (5) 9 8 (7)
5<7,无需交换
结果:4 5 9 8 7
自此,第二小的值5已被确定,下一轮
第一:
比较 4 5 (9) (8) 7
9>8,所以交换9和8
结果:4 5 8 9 7
第二:
比较 4 5 (8) 9 (7)
8>7,所以交换8和7
结果:4 5 7 9 8
自此,第三小的值7已被确定,下一轮
第一:
比较 4 5 7 (9) (8)
9>8,所以交换9和8
结果:4 5 7 8 9
自此,第四小的值8已被确定,由于已知第n-1小的值,就不需要再计算第n大的值,所以排序程序结束
OK,所有的数值排好序如下:
(五)总结
选择排序就相当于一种打擂台的过程,也跟冒泡排序比较类似,常被人混淆。它将未知的数据与已知的(有序的)进行挨个比较,最后可以确定出一串有序的序列。
最强排序算法——快速排序
(一)快速排序原理
基于分治策略的排序算法。 通过选择一个基准元素 将待排序序列中所有小于它的排至它的左边 将待排序序列中所有大于它的排至它的右边 随后对两个子序列分别进行快速排序(递归)
优点:平均时间复杂度较快
缺点:最坏情况时间复杂度高,且需要空间来存储递归,调用栈
适用于大规模的数据排序,特别是需要稳定排序算法的情况下
(二)复杂度分析
时间复杂度:O(n*log n)——平均情况 O(n*n)——最坏情况 空间复杂度:O(log n)——递归深度 O(1)——辅助空间
(三)代码实现
#include<iostream> #include<cstdio> using namespace std; int a[100005],n;//定义数组,个数 void quicksort(int left,int right)//定义函数,快速排序 { if(left>right)//特判一下,左小于右就返回(递归出口) { return; } int i,j,temp;//i和j分别代表左和右,挨个枚举,temp则存储基准值 temp=a[left]; i=left; j=right; while(i<j) { //需要从右往左找(顺序不能反) while(a[j]>=temp&&i<j) { j--; } //再从左往右找 while(a[i]<=temp&&i<j) { i++; } if(i<j)//交换这两个数在数组中的位置 { swap(a[i],a[j]); } } //基准数归位 a[left]=a[i]; a[i]=temp; quicksort(left,i-1);//递归,继续处理左边 quicksort(i+1,right);//递归,继续处理右边 } int main(){ cin>>n;//输入 for(int i=1;i<=n;i++)//输入 { cin>>a[i]; } quicksort(1,n);//调用快速排序函数,从1到n排序 for(int i=1;i<=n;i++)//输出 { cout<<a[i]<<" "; } cout<<endl; return 0; }
(四)模拟过程
假设输入10个数据:
6 1 2 7 9 3 4 5 10 8
数组a的变化过程(括号内表示已归位的基准数):
6 1 2 7 9 3 4 5 10 8 (一开始)
3 1 2 5 4 (6) 9 7 10 8
2 1 (3) 5 4 6 9 7 10 8
1 (2) 3 5 4 6 9 7 10 8
(1) 2 3 5 4 6 9 7 10 8
1 2 3 4 (5) 6 9 7 10 8
1 2 3 (4) 5 6 9 7 10 8
1 2 3 4 5 6 8 7 (9) 10
1 2 3 4 5 6 7 (8) 9 10
1 2 3 4 5 6 (7) 8 9 10
1 2 3 4 5 6 7 8 9 (10)
至此,程序运行结束,排序完成
真题运用:
[NOIP2006 普及组] 明明的随机数
本题就是考察我们对排序算法的掌握情况,可以将快速排序代码稍加修改后解决,如下:
#include<iostream> #include<cstdio> using namespace std; int a[100005],n;//定义数组,个数 int ans=0;//定义一个题目要我们最后提出的去重后的数量 void quicksort(int left,int right)//定义函数,快速排序 { if(left>right)//特判一下,左小于右就返回(递归出口) { return; } int i,j,temp;//i和j分别代表左和右,挨个枚举,temp则存储基准值 temp=a[left]; i=left; j=right; while(i<j) { //需要从右往左找(顺序不能反) while(a[j]>=temp&&i<j) { j--; } //再从左往右找 while(a[i]<=temp&&i<j) { i++; } if(i<j)//交换这两个数在数组中的位置 { swap(a[i],a[j]); } } //基准数归位 a[left]=a[i]; a[i]=temp; quicksort(left,i-1);//递归,继续处理左边 quicksort(i+1,right);//递归,继续处理右边 } int main(){ cin>>n;//输入 for(int i=1;i<=n;i++)//输入 { cin>>a[i]; } quicksort(1,n);//调用快速排序函数,从1到n排序 for(int i=1;i<=n;i++)//数量统计环节 { if(a[i]!=a[i-1])//由于是排过序的数组,所以可直接判断前后是否相等 { ans++; } } cout<<ans<<endl;//输出 for(int i=1;i<=n;i++)//输出 { if(a[i]!=a[i-1]) { cout<<a[i]<<" "; } } cout<<endl; return 0; }
如愿以偿,得到了100
(五)总结
快速排序其实是个选择排序和递归的结合体,运用二分的思想,将交换跳跃式进行,速度会比之前的选择排序快得多。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了