简单排序
通常我们在获取了一些数据之后会对这些数据进行处理,常用的操
作有排序,查找,插入,删除等等,本节给大家介绍两种基本的排
序方法:冒泡排序,插入排序、选择排序、桶排序。
冒泡排序:
输入n个数,将这n个数从小到大输出。
冒泡排序的思想:以n个人站队为例,从第1个开始,依次比较相
邻的两个是否逆序(高在前,矮在后),若逆序就交换这两人,即
第1个和第2个比,若逆序就交换两人,接着第2个和第3个比,若
逆序就交换两人,接着第3个和第4个比,若逆序就交换两人,…
…,直到n-1和n比较,经过一轮比较后,则把最高的人排到最后,
即将最高的人像冒泡一样逐步冒到相应的位置。原n个人的排序问
题,转换为n-1个人的排序问题。第二轮从第1个开始,依次比较
相邻的两个人是否逆序,若逆序就交换两人,直到n-2和n-1比较。
如此,进行n-1轮后,队列为有序的队列。
从上述分析中可以看出,每进行一轮的比较之后,n个数的排序规模就转化为n-1个数的排序规模。
例如有6个元素需要排序:
6 5 3 4 1 2
第一趟排序:
第二趟排序:
第三趟排序:
第四趟排序:
第五趟排序:
五趟结束后,6个整数就已经排序完成。排序过程中,大数慢慢的
往后,相当于气泡上升,所以叫冒泡排序。
归纳上述排序过程,具体实现步骤如下:
(1)读入数据存放在a数组中。
(2)对数组的第1个数据到i个数据进行一次遍历,比较相邻的前后
两个数据,如果前面数据大于后面的数据,就将两个数据交换,
最大的一个数据就“冒”到数组第i个位置。
(3)第i个位置的数确定后,i—,确定第i-1个位置,如果i为1,排
序完成,否则重复第二、第三步。
程序实现方法:用两层循环完成算法,外层循环i控制每轮要进
行多少次的比较,第1轮比较n-1次,第2轮比较n-2次,……,
最后一轮比较1次。内层循环j控制每轮i次比较相邻两个元素是
否逆序,若逆序就交换这两个元素。
【程序实现】
#include<iostream>
using namespace std;
int a[10010];
int main()
{
int n,t;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=n-1;i>=1;i--) //进行n-1轮冒泡
for(int j=1;j<=i;j++) //每次进行i次比较
{
if(a[j]>a[j+1]) //相邻元素比较,逆序就交换
{
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
for(int i=1;i<=n;i++) //输出结果
cout<<a[i]<<" ";
return 0;
}
插入排序:
插入排序思想:回忆一下玩扑克牌时抓牌的情景,为了方便打牌,
抓牌时一般一边抓牌一边按花色和大小插入恰当的位置,当抓完
所有的牌时,手中的牌便是有序的,这种排序方法即插入排序。
当读入一个元素时,在已经排序好的序列中,搜寻它正确的位置
,再放入读入的元素。但不该忽略一个重要的问题:在插入这个
元素前,应当先将它后面的所有元素后移一位,以保证插入位置
的原元素不被覆盖。
例如:设n=8,数组a中8个元素是: 36,25,48,12,65,43,20,58,执
行插入排序后,其数据变动情况(用[ ]括起来的数字表示已经
排好序,_表示插入的数据):
第0步:[36] 25 48 12 65 43 20 58
第1步:[25 36] 48 12 65 43 20 58
第2步:[25 36 48] 12 65 43 20 58
第3步:[12 25 36 48] 65 43 20 58
第4步:[12 25 36 48 65] 43 20 58
第5步:[12 25 36 43 48 65] 20 5
第6步:[12 20 25 36 43 48 65] 5
第7步:[12 20 25 36 43 48 58 65]
归纳上述排序过程,具体实现步骤如下:
读入数据存放在a数组中。
对第i个位置的数a[i]进行插入
从排好序的1~i-1个数从左往右遍历一遍,找到第一个比a[i]
大的数,记录下当前位置t。
将t之后的元素,t~i-1全部往后移动一个位置,第t个位置存放
a[i]。i增加1 ,继续插入,重复第二到第四步。
#include<iostream>
using namespace std;
int a[10010];
int main()
{
int n,t,temp;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++) //将第i个数a[i]进行插入
{
t=i;temp=a[i]; //t表示找到的合适位置,默认t为本身位置 ,如果没找到比a[i]大的数,就插入原位置,,temp表示要插入的数a[i]
for(int j=1;j<=i-1;j++) //在已经排好序的1~i-1中找合适位置
{
if(a[j]>a[i]) //如果找到第一个比a[i]大的数就是合适位置 ,退出循环
{
t=j; //a[i]就插入t这个位置,t以后的元素全部往后移动一个位置
break;
}
}
for(int j=i-1;j>=t;j--) //将t~i-1往后移动一个位置
a[j+1]=a[j];
a[t]=temp; //插入
}
for(int i=1;i<=n;i++) //输出结果
cout<<a[i]<<" ";
return 0;
}
选择排序:
选择排序的思想就是,第一小的在第一个位置,第二小的在第二个
位置,…,第n小的在第n个位置,这样就能实现n个数的排序。
算法流程:
1.每一次排序取出待排序列的第一个数x作为基准数,通过遍历找
到比x更小的数,然后交换
2.接下来在剩下的待排序列中,重复第一步。这样,有序序列不
断变长,待排序列不断变短,n趟排序后整个序列有序
【程序实现】
#include <iostream>
using namespace std;
int main()
{
int i,j,n,k,temp,a[101];//k记录最小值的下标,temp为交换变量
cin>>n;
for(i=1;i<=n;i++) cin>>a[i];
//选择排序
for(i=1;i<=n;i++){ //n趟排序
k=i; //以待排序列的第一个数为基准数
for(j=i+1;j<=n;j++){ //从基准数的下一个数开始找
if(a[j]<a[k]) //找更小的值
k=j; //更新最小值下标
}
temp=a[i]; //与待排序列第一个数交换
a[i]=a[k];
a[k]=temp;
}
for(i=1;i<=n;i++)
cout<<a[i]<<" ";
return 0;
}
桶排序:
桶排序的思想,把数据当做小球,设置一系列的桶,对应的小球放
到对应的桶里。
算法流程:
1.设置一个数组,数组每一个空间就是一个桶,将数组下标做为桶
的编号。
2.遍历输入数据,并且把数据一个一个放到对应的桶里去。数据的
编号就是数据的值,比如1就放到a[1],100就放到a[100]。
3.顺序遍历整个数组,访问每个桶,将不是空的桶的编号输出,就
是一个有序的数组了。
从这个流程其实还可以看出,在输出的时候数据只会输出一次,所
以桶排序还有去重的功能
虽然桶排序比其他的排序快的多,但它的数组大小是由数据的大小
来决定的。也就是说大数据很大的时候,桶排序是不适用的。
从桶排序也可以看出,一个算法的时间复杂度与空间复杂度很难都
很优秀。要么空间换时间,要么时间换空间。
【程序实现】
#include <iostream>
using namespace std;
int main()
{
int i,x,n,maxn=-1,a[10001]; //数组a就是一个桶,maxn记录数据的最大值
cin>>n; //数据个数
for(i=1;i<=n;i++) {
cin>>x;
a[x]++; //对应的数据放到对应的桶
if(maxn<x) maxn=x;
}
for(i=1;i<=maxn;i++) //遍历桶的范围是数据的最大值,而不是数据的个数
if(a[i]!=0)
cout<<i<<" ";
return 0;
}
练习
分数线划定:世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才,A 市对所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试。面试分数线根据计划录取人数的150%划定,即如果计划录取m名志愿者,则面试分数线为排名第m*150%(向下取整)名的选手的分数,而最终进入面试的选手为笔试成绩不低于面试分数线的所有选手。
现在就请你编写程序划定面试分数线,并输出所有进入面试的选手的报名号和笔试成绩。
【输入】
第一行,两个整数n,m(5 ≤ n ≤ 5000,3 ≤ m ≤ n),中间用一个空格隔开,其中n 表示报名参加笔试的选手总数,m 表示计划录取的志愿者人数。输入数据保证m*150%向下 取整后小于等于n。
第二行到第 n+1 行,每行包括两个整数,中间用一个空格隔开,分别是选手的报名号k(1000 ≤ k ≤ 9999)和该选手的笔试成绩s(1 ≤ s ≤ 100)。数据保证选手的报名号各不相同。
【输出】
第一行,有两个整数,用一个空格隔开,第一个整数表示面试分数线;第二个整数为进入面试的选手的实际人数。
从第二行开始,每行包含两个整数,中间用一个空格隔开,分别表示进入面试的选手的报名号和笔试成绩,按照笔试成绩从高到低输出,如果成绩相同,则按报名号由小到大的顺序输出。
【样例输入】
6 3
1000 90
3239 88
2390 95
7231 84
1005 95
1001 88
【样例输出】
88 5
1005 95
2390 95
1000 90
1001 88
3239 88
提示:
m150% = 3150% = 4.5,向下取整后为4。保证4 个人进入面试的分数线为88,但因为88有重分,所以所有成绩大于等于88 的选手都可以进入面试,故最终有5 个人进入面试。
题目大意 将面试选手的报名号和笔试成绩,先按照笔试成绩从高到低输出,如果成绩相同,再按照报名号由小到大的顺序输出。
思路分析 主要是排序算法的条件有所变化,不再是简单的比较分数的大小,还需要考虑分数相同时,按照报名号大小进行排序,这就需要更改排序算法的判断条件。我们可以选择两个数组来a,b来分别存储分数和报名号,a[i],b[i]就表示第i个人的分数和报名号,在交换变量的时候一定要a[i]和b[i]同时交换。
数据排序后,我们要选择k*1.5位选手进入面试,但可能会出现提示里所说的同分的情况,所以我们不能直接输出,还需要继续判断后面是否还有分数相同的选手,确定实际进入面试的选手人数。
参考代码
#include <iostream>
using namespace std;
int main()
{
int i,j,n,k,temp,m; //temp为交换变量,m保存第k位的分数
int a[5005],b[5005]; //数组a存储分数,数组b存储报名号
cin>>n>>k;
for(i=1;i<=n;i++) cin>>b[i]>>a[i]; //输入报名号和分数
for(i=1;i<=n-1;i++) //冒泡排序
for(j=1;j<=n-i;j++){
if(a[j]<a[j+1]) //按分数从大到小
{
temp=a[j]; //交换a,b
a[j]=a[j+1];
a[j+1]=temp;
temp=b[j];
b[j]=b[j+1];
b[j+1]=temp;
}
else if(a[j]==a[j+1]&&b[j]>b[j+1]) //分数相同,按报名号从小到大
{
temp=a[j]; //交换a,b
a[j]=a[j+1];
a[j+1]=temp;
temp=b[j];
b[j]=b[j+1];
b[j+1]=temp;
}
}
k=k*1.5; //进入面试人数k*1.5
m=a[k]; //第k位的分数
while(m==a[k]) k++; //可能存在与k位相同分数的选手
k--; //while循环结束时,k其实多加了1,要减去
cout<<a[k]<<" "<<k<<endl; //输出答案
for(i=1;i<=k;i++)
cout<<b[i]<<" "<<a[i]<<endl;
return 0;
}
排序就讲到这里了,还请各位大佬归我个赞。
这上面提到的冒泡排序有点耗时间,最好进新一些优化,优化的代码以及讲解如下(一个链接):
点我