1.计数排序
计数排序的方法是先计算出每个数字的个数然后在进行一个一个的排序。
设需要排最小数为m,最大数为n的数,一共有x个数,y=n-m,那么读入的时间复杂度为o(x),输出时的时间复杂度为o(x+y),空间复杂度为o(y)。
例题:洛谷 P1271 【深基9.例1】选举学生会
题目描述
学校正在选举学生会成员,有 n(n≤999) 名候选人,每名候选人编号分别从 1 到 n,现在收集到了 m(m<=2000000) 张选票,每张选票都写了一个候选人编号。现在想把这些堆积如山的选票按照投票数字从小到大排序。
输入格式
输入 n 和 m 以及 m 个选票上的数字。
输出格式
求出排序后的选票编号。
代码如下:
#include <iostream>
using namespace std;
int main()
{
int m,n,i,j,c;
cin >>n>>m;
int a[n+1]={0};
for(i=0;i<m;i++)
{
cin >>c;
a[c]++;
}
for(i=1;i<=n;i++)
for(j=0;j<a[i];j++)
cout <<i<<' ';
return 0;
}
计数排序通常用不了很大数据的排序中,只能用于整数数据排序中,而关于浮点数和字符串的排序中则不能使用。
2.选择排序
选择排序的方法是从整个数据中找到最小的那个数与第一个数交换,再从接下来的数据中找到最小的那个属于第二个数交换,依次排序。
设整个数据一共有n个,则选择排序的时间复杂度为o(n²),空间复杂度为o(n)。
例题:洛谷 P1059 [NOIP2006 普及组] 明明的随机数
题目描述
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。
输入格式
输入有两行,第1行为1个正整数,表示所生成的随机数的个数N
第2行有N个用空格隔开的正整数,为所产生的随机数。
输出格式
输出也是两行,第1行为1个正整数M,表示不相同的随机数的个数。
第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。
代码如下:
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
int N,i,j,b,c,min;
cin >>N;
int a[N],d[N];
for(i=0;i<N;i++) cin >>a[i];
for(i=0;i<N-1;i++)//这段代码是选择排序。
{
min=a[i];
b=i;
for(j=i+1;j<N;j++)
if(min>a[j]) {min=a[j];b=j;}
c=a[i];a[i]=a[b];a[b]=c;
}
d[0]=a[0];//接下来需要按照题目要求去重。
for(i=1,j=1;i<N;i++)
{
if(a[i-1]==a[i]) continue;
else {d[j]=a[i];j++;}
}
cout <<j<<endl;
for(i=0;i<j;i++)
cout <<d[i]<<' ';
return 0;
}
选择排序在交换数据位置过程中,会造成相同数据的相对前后顺序遭到破坏,所以选择排序是一个比较不稳定的排序方法。
3.冒泡排序
冒泡排序的方法是从第一个和第二个相互比较并排序,再比较第二个和第三个并排序,以此类推,那么最大的那个数就会跑的最后面去,再循环一遍剩下的数据中最大的又跑到最后面,以此类推,直到循环的数据只剩下第一个,排序结束。
设整个数据一共有n个,则选择排序的时间复杂度为o(n²),空间复杂度为o(n),和选择排序的时间复杂度和空间复杂度差不多。
例题:洛谷 P2094 运输
题目描述
现在已知 N 件商品,和搬运它们其中每一件的费用。现在搬家公司老板 Mr.sb 决定让我们每次任意选取 2 件商品。然后这 2 件商品只算一件商品的费用。但是这个商品的搬运费用是将选出的 2 个商品的费用之和除以 $k 的运算结果。如此反复。直到只收一件商品的钱。这个就是商店要付的费用。掌柜的想尽可能的少付钱,以便将更多的钱捐给希望工程。所以请你帮他计算一下最少只用付多少钱。
输入格式
第一行两个整数 n,k。
第二行 n 个整数 w1,w2,…,wn,表示每一件物品搬运费。
输出格式
一行一个整数表示最少付多少钱。
代码如下:
#include <iostream>
using namespace std;
int main()
{
int n,i,j,c,k,temp;
cin >>n>>k;
int a[n];
for(i=0;i<n;i++) cin >>a[i];
for(i=0;i<n-1;i++)//这里使用冒泡排序的方法
{
for(j=0;j<n-i-1;j++)
{
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
for(i=n-1;i>0;i--)//这里根据题目理解计算商品最少用多少钱
{
a[i-1]=(a[i-1]+a[i])/k;
c=a[i-1];
if(i==1) continue;
j=i-2;
while(c<a[j])
{
a[j+1]=a[j];
j--;
}
a[j+1]=c;
}
cout <<a[0]<<endl;//输出结果
return 0;
}
4.插入排序
插入排序的方法是在已排好序的数据中再插入数据,比如前n-1个数已经排好序,插入第n个数,然后使得这n个数排好序,按照这个方法把剩下所有数据排好序。
插入排序的时间复杂度为o(n²),空间复杂度为o(1)。
例题:洛谷 P1094 [NOIP2007 普及组] 纪念品分组
题目描述
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得 的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品, 并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。
你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
输入格式
共 n+2 行:
第一行包括一个整数 w,为每组纪念品价格之和的上上限。
第二行为一个整数 n,表示购来的纪念品的总件数 G。
第 3∼n+2 行每行包含一个正整数 Pi 表示所对应纪念品的价格。
输出格式
一个整数,即最少的分组数目。
代码如下:
#include <iostream>
using namespace std;
int main()
{
int w,n,i,j,temp,sum=0,t=0;
cin >>w>>n;
int a[n];
for(i=0;i<n;i++) cin >>a[i];
for(i=0;i<n;i++)//这里使用插入排序
{
for(temp=a[i],j=i-1;j>=0;j--)
{
if(a[j]>temp)
a[j+1]=a[j];
else break;
}
a[j+1]=temp;
}
//这里根据题意说分组最少的那个
i=0;j=n-1;//从两边往中间开始看
while(i<j)
{
if(a[i]+a[j]<=w)//当较大数和较小数相加不超过给定数时
{
i++;j--;sum++;t+=2;//同时往中间聚
}
else {j--;sum++;t++;}//当相加超过给定数时,较大数往中间聚拢
}
if(t<n) sum++;//但中间那个数没取到时,再加一。
cout <<sum<<endl;
return 0;
}
5.快速排序
快速排序的方法
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
快速排序的时间复杂度为o(n²),实际上快速排序的时间复杂度为o(n㏒ n),空间复杂度为o(n)。
例题:洛谷 P1177 【模板】快速排序
题目描述
利用快速排序算法将读入的 N 个数从小到大排序后输出。
输入格式
第 1 行为一个正整数 N,第 2 行包含 N 个空格隔开的正整数 ai,为你需要进行排序的数,数据保证了 Ai 不超过 10^9。
输出格式
将给定的 N 个数从小到大输出,数之间空格隔开,行末换行且无空格。
代码如下:
#include <iostream>
using namespace std;
void pai(int a[],int x,int y);
int main()
{
int n,i;
cin >>n;
int a[n];
for(i=0;i<n;i++)
cin >>a[i];
pai(a,0,n-1);
for(i=0;i<n;i++)
cout <<a[i]<<' ';
return 0;
}
void pai(int a[],int x,int y)
{
int i=x,j=y,flag=a[(x+y)/2]/*这里用中间量做分界值*/,temp;
do
{
while(a[i]<flag) i++;//寻找比分界值大的数
while(a[j]>flag) j--;//寻找比分界值小的数
if(i<=j)
{
temp=a[i];a[i]=a[j];a[j]=temp;//交换数据
i++;j--;
}
}
while(i<=j);
if(x<j) pai(a,x,j);//这里使用递归
if(y>i) pai(a,i,y);
}
快速排序所应用的数据量比较大,可以实现数据量很大的排序。