常见的排序算法有冒泡排序,选择排序,堆排序,快速排序这几种,但十三写博客的目的是为了提高自己的编程解题能力,故在这里介绍如何能快速解决那些涉及到排序的编程问题,首先针对这些问题C++ STL库中已经有了sort函数可以供我们直接调用。
sort()函数
使用sort()函数必须需要加上头文件#include和using namespace std; 其使用的格式如下:
sort(首元素地址, 尾元素地址的下一个地址, cmp)
其中上面第三个元素cmp为非必填元素,cmp为比较函数,其设置目的为了按照程序设计者自己的思路排序(并不是按照从小到大的默认顺序排)于博主而言,cmp在sort()函数中有两个直接的作用,一是很轻松就可以做到从大到小的排列顺序,二是可以直接排序结构体中成员某些特性(PAT经常考察对结构体变量进行排序),其书写的大致逻辑如下:
bool cmp(int a,int b){
return a > b;
}
bool cmp(node a,node b){
return a.x > b.x;
}
同样STL标准容器中只有vector,string,deque可以使用sort(),而set,map这种本就有序的容器,不允许使用sort排序。
经典习题
PAT B1015/A1062
题目链接
题目描述
先输入考生数目,两种成绩的及格分与优秀分,再输入一批考生的id,德分和才分,再依据输入考生的德才分进行分类排序,依据如下:
-
两门课中一门未达到及格线的考生划到第五类
-
两门分数均超过优秀分的为第一类考生,第一类考生内部再由德才总分进行排序
-
德分超过优秀而才分未达线者华为第二类考生,其内部亦按照德才总分排序
-
德才均小于优秀分划为第三类考生,其内部亦按照德才总分排序
-
其余为第四类
n个考生的排序规则:
- 先按照类别从小到大分类
- 类别相同,按照总分从大到小排序
- 相同类别,相同总分,优先输出德分高的考生
- 相同类别,相同总分,相等德分,有限输出准考证号小的
思路
使用sort()排序,cmp函数规则可以按照考生排序规则来写:
bool cmp(student a,student b){
if(a.flag != b.flag) return a.flag < b.flag;
else if(a.sum != b.sum) return a.sum > b.sum;
else if(a.virtue != b.virtue) return a.virtue > b.virtue;
else return strcmp(a.id, b.id) < 0;
}
代码
#include <iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct student{
char id[10];
int virtue;
int talent;
int sum;
int flag;
};
bool cmp(student a, student b){
if(a.flag != b.flag) return a.flag < b.flag;
else if(a.sum != b.sum) return a.sum > b.sum;
else if(a.virtue != b.virtue) return a.virtue > b.virtue;
else return strcmp(a.id, b.id) < 0;
}
int main()
{
int n,low,high;
cin >> n >> low >> high;
struct student stu[n];
int m=n;
for(int i = 0; i<n; i++){
cin >> stu[i].id >> stu[i].virtue >> stu[i].talent;
stu[i].sum = stu[i].virtue + stu[i].talent;
if(stu[i].virtue<low||stu[i].talent<low) {
stu[i].flag = 5;
m--;
}
//分类的步骤
else if(stu[i].virtue>=high&&stu[i].talent>=high) stu[i].flag = 1;
else if(stu[i].virtue>=high&&stu[i].talent<high) stu[i].flag = 2;
else if(stu[i].virtue>stu[i].talent) stu[i].flag = 3;
else stu[i].flag = 4;
}
sort(stu, stu+n,cmp);//直接将n个元素按照cmp中规定好的顺序排序
cout << m << endl;
for(int i=0;i<m;i++){
cout << stu[i].id << ' ' << stu[i].virtue << ' ' << stu[i].talent << endl;
}
return 0;
}
如果你那我的代码往PAT上面跑通不过hhh,肯定是超时的,不过这并不妨碍这道题作为排序算法的经典习题。
总结
为什么把本题作为排序算法的例题,因为本题排序中也有排序,读者可以点开原题链接看看原文,原文描述地云里雾里的,需要读者自己把题意抽象成一个排序中排序的模型,十三我自己开始做的时候,不愿意使用flag分类,心想最后按照要求输出不就可以了嘛,何必多此一举在结构体里面多定义一个变量占内存呢?在参考了网上大佬的代码后,立flag是为了初始分类,为了进一步使用cmp排序规则埋下伏笔的!cmp+sort()可真的是个宝贝啊,一下就解决了在我脑子里面构想的该使用哪种排序算法来解决这个问题的问题。
PAT A 1025
题目链接
题目描述
要求最先输入考场数目n,再依次输入每个考场的考生数目k,考生的id,分数,最后输出总体考生数,并按照考生分数从高到低的次序输出每一个考生的id,总排名,考场号,所在考场排名,这里需要注意的是,分数一致的考生名次相同,下一位考生的名次则等于他所处的相对位置。
Sample Input:
2
5
1234567890001 95
1234567890005 100
1234567890003 95
1234567890002 77
1234567890004 85
4
1234567890013 65
1234567890011 25
1234567890014 100
1234567890012 85
Sample Output:
9
1234567890005 1 1 1
1234567890014 1 2 1
1234567890001 3 1 2
1234567890003 3 1 2
1234567890004 5 1 4
1234567890012 5 2 2
1234567890002 7 1 5
1234567890013 8 2 3
1234567890011 9 2 4
思路
本题的排序方面的方法同A 1062,有两方面排序,一个是考场内排序,另外一个是全体考生排序,其排序规则可由cmp函数决定,其排名依据有以下两点:
- 不同分数,按照分数从大到小排
- 相同分数,按照id从小到大排
bool cmp(Student a,Student b){
if(a.scores != b.scores)return a.scores > b.scores;//不同分数按照从大到小
else return strcmp(a.id,b.id) < 0;//相同分数,按照id从小到大
}
坦白说,相同分数按照id从小到大排序,这句话在题目中并未提及(或者是十三太菜没看见)十三我在提交这道题时,不加这句话,结果总是部分正确,在这里吐槽一下PTA测试平台,只告知测试点过不去,却不告知哪一个测试集没过去,这让十三找bug找的很辛苦的啊!
本题不仅仅需要对考生进行排序,还需要每个考生在所在考场,所有考场的排名,这就有点像十三上高中时,考完试时时刻刻关注着自己的班级排名,年级排名,那如何由排序好的考生得出他们各自的排名呢?分数最高的肯定时第一名,后一名的分数如果与前一名相同,则他俩名次相同,否则考生的名次就是他前面的考生数+1,以下是十三对全体考生赋予名次的代码:
stu[0].fra = 1;
for(int i=1;i<m;i++){
if(stu[i].scores == stu[i-1].scores) stu[i].fra = stu[i-1].fra;
else stu[i].fra = i+1;
}
我的代码
```cpp
#include <cstdio>
#include<cstring>
#include<algorithm>
struct Student{
char id[14];
int scores;
int fra,lora;//总排名与区域排名
int location;
}stu[30000];
/*比较规则*/
bool cmp(Student a,Student b){
if(a.scores != b.scores)return a.scores > b.scores;//不同分数按照从大到小
else return strcmp(a.id,b.id) < 0;//相同分数,按照id从小到大
}
using namespace std;
int main()
{
int n,m=0;
scanf("%d",&n);
/*input data*/
for(int i=0;i<n;i++){
int k;//第i+1考场的考生数
scanf("%d",&k);
for(int j=0;j<k;j++){
scanf("%s%d",stu[m+j].id,&stu[m+j].scores);//这里给第i+1个考场的考生依次赋值
stu[m+j].location = i+1;
}
sort(stu+m,stu+m+k,cmp);//对当前考场k个考生进行排序
stu[m].lora = 1;
for(int t=1;t<k;t++){
if(stu[m+t].scores == stu[m+t-1].scores) stu[m+t].lora = stu[m+t-1].lora;
else stu[m+t].lora = t+1;
}
m +=k;//m是用来计算已输入考场的考生数目
}
sort(stu,stu+m,cmp);//对全体考生进行排序
stu[0].fra = 1;
for(int i=1;i<m;i++){
if(stu[i].scores == stu[i-1].scores) stu[i].fra = stu[i-1].fra;
else stu[i].fra = i+1;
}
printf("%d\n",m);
for(int i=0;i<m;i++){
printf("%s %d %d %d\n",stu[i].id,stu[i].fra,stu[i].location,stu[i].lora);
}
return 0;
}