排序算法大合集

排序算法大合集

翻了翻很久以前写的算法报告,现在整理一下。

由难度从简单到难排序。

桶排序、冒泡排序、选择排序、快速排序。

最简单粗暴——桶排序

(一)桶排序原理

桶排序,是一个目前速度最快的一种排序
基本思想是将无序列数依次装进一个按元素名命名数组中
最后挨个判断每个“桶”中有没有数
有的话将其输出的一种快速排序
优点:速度最快
缺点:只限用于小数据排序,通常会造成巨大的空间损失
适用于小规模且有一定数据范围的数据排序

(二)复杂度分析

时间复杂度: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,所有的数值排好序如下:

45789

(五)总结

选择排序就相当于一种打擂台的过程,也跟冒泡排序比较类似,常被人混淆。它将未知的数据与已知的(有序的)进行挨个比较,最后可以确定出一串有序的序列。

最强排序算法——快速排序

(一)快速排序原理

基于分治策略的排序算法。
通过选择一个基准元素
将待排序序列中所有小于它的排至它的左边
将待排序序列中所有大于它的排至它的右边
随后对两个子序列分别进行快速排序(递归)

优点:平均时间复杂度较快

缺点:最坏情况时间复杂度高,且需要空间来存储递归,调用栈

适用于大规模的数据排序,特别是需要稳定排序算法的情况下

(二)复杂度分析

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

(五)总结

快速排序其实是个选择排序和递归的结合体,运用二分的思想,将交换跳跃式进行,速度会比之前的选择排序快得多。
posted @   Atserckcn  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示