题目描述
Zero 是一名学习委员,他负责很多有关学习上的任务,今天辅导员给了他一张成绩单,这个成绩单是按学号排序的,但是它是成绩单,应该按成绩排序。Zero 作为一个 acmer,对排序还算了解,但他想考考你,你能完成这个任务吗?
输入
一个正整数 n,代表成绩单上学生的人数(n <= 2000)
接下来 n 行,每行两个整数 ID 和 x,ID是学生的编号(递增给出),x 是学生成绩 (0 <= x <= 100)
对于输入的数据,第一个整数n代表成绩单上学生的人数(即告诉我们需要存储的数据的量有:n个学号和n个成绩)
接下来的n行,每行会输入两个整数分别代表学生的学号和成绩,有n行,自然要有n次读入,所以我们用循环读入,将读入的学号与成绩分别存入两个数组中(id与score)。每次循环读入两个数,分别存入id与score数组下标相同的位置,表示它们是同一个学生的信息。
全部读入完成后,就要对它们进行排序了(有一说一但凡能用Excel,这破代码是一行都不想多敲的)。
排序(重点)
对于数组的排序,最基础的当属冒泡排序(或许一点也不基础,但确实是最早接触到的)
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
看不懂对不对?看不懂就对了,如果能看懂那我也不用大费周章地写这篇题解给你看了。
再来看这个动图演示
依旧看不懂对吧?那就不搞这些虚头八脑的东西了,好好看我写的题解。
排序不会,但比较大小和交换位置这两个操作,总该是会的吧
当给出一个数列:2 6 3 4 1,让你通过比较大小和交换位置,将这个数列变成递增序列,你会怎么做?
对喽,拿两个数比较它们的大小,如果前面的数大,就把后面的数放前面,前面的数放后面嘛
可是计算机不知道该拿哪两个数去比较怎么办?这还不好说,让它把所有数都比一遍就好啦
就以刚刚的2 6 3 4 1为例,我们先让2和6比大小,发现前面的2比后面的6要小,不用交换位置,然后再让6和3去比较,很明显,前面的6要比后面的3大,我们交换它们俩的位置,数列变成了2 3 6 4 1,下一步比较6和4的大小,交换6和4,数列变成了2 3 4 6 1,再比较6和1,交换6和1,数列变成了2 3 4 1 6。
经过这轮操作,可以发现最大的6被换到了数列的最后面,如果我们再从头去进行比较、交换的操作,第二轮操作结束后,数列会变成2 3 1 4 6,第二大的4被放到了它该在的位置,那再来一轮呢?数列会变成2 1 3 4 6,第三大的3也被放到了它应该在的位置,这时我们不妨大胆猜测,每一轮操作,都会有一个数字被放到它应该在的位置。而第 i 轮被确定位置的数,就是这个数列中第 i 大的数。
这个猜测很大胆,但听起来也很合理,我们任意举的例子也都会符合这个猜测。那么,新的问题来了,我们最多需要进行多少轮操作,才能使这个数列变得有序呢?
如果每轮操作会将一个数字放到它该在的位置的话,要使有n个数字的数列变得有序,看起来似乎需要n轮操作,但是对于有n个数字的数列来说,它一共只有n个用于放数字的位置,如果有n - 1个数字都被放到了自己该在的位置,那剩下的一个数字既然还在这个数列里,那就一定在它应该在的位置上对叭。
所以,我们最多只需要进行n - 1轮操作,就可以将有n个数字的数列变得有序咯。
理论成立,让我们用代码来实现一下。
#include <stdio.h>
int a[110]; //定义一个数组用来存储数列中的元素,数组稍微开大一些
int main()
{
int n, i, j; // i,j均为循环变量
scanf("%d",&n); //变量n用于记录数列中一共有多少个元素
for(i = 1; i <= n; i ++) //数组从下标为1的地方开始存储,a[1]即为数组的第一个元素
{
scanf("%d",&a[i]);
}
//此处的i用于记录当前进行到了第几轮循环,共需进行n - 1次循环
for(i = 1; i <= n - 1; i ++)
{
//j作为数组下标,每轮循环的每次操作,比较数组中第j个元素和第j + 1个元素的大小
/*注意此处j的范围,每轮操作结束会确定一个数的位置,位置已经确定的数不需要参与后续的操作
另外我们每次比较的是第j个元素和第j个元素后面的元素(j + 1),所以j只需要枚举从1到未确
定位置的数的个数减一即可,比如刚开始有五个数,我们第一轮只需要比较四次,第二轮需要比较
三次(5 - 2次)。*/
for(j = 1; j <= n - i; j ++)
{
//若前面的数大于后面的数,则交换两个数的位置
if(a[j] > a[j + 1])
{
int b = a[j];
a[j] = a[j + 1];
a[j + 1] = b;
}
}
}
//输出排好序的数列(数组)
for(i = 1; i <= n; i ++)
{
printf("%d ", a[i]);
}
return 0;
}
当学会上述算法(冒泡排序)后,我们就可以继续看这道明明可以用Excel解决却偏让我们去写代码的题了
还记得题目给了我们什么吗?对喽,一个整数n和n个学生的学号及成绩。
我们用变量n来存储学生的个数,用数组id来存储学生的学号,数组score来存储学生的成绩
然后根据题目要求进行排序操作
话说题目的要求是什么来着?噢对了,我还没放上来
输出
输出按成绩排名的成绩单,成绩越高,排名越靠前,相同成绩的人 ID 较小的排名在前
既然要按成绩排名输出成绩单,我们自然要按成绩高低来排序
需要注意的是,在我们按照成绩排序的时候,交换成绩数组中元素位置的同时,学号也要跟着对应的成绩去交换,否则输出的时候会发现一个人的成绩和它的学号对不上号(这样搞的话就不是一个好班长咯)
另外,成绩排名,自然是成绩越高排名越靠前,所以我们要把分高的人放到最前面,注意不要搞反了哟
代码实现如下
#include<stdio.h>
int id[2010], score[2010];
int main()
{
int n, i, j; //i,j为循环变量
scanf("%d", &n);
for(i = 1; i <= n; i ++)
{
scanf("%d%d", &id[i], &score[i]); //读入第i个学生的学号和成绩分别存入id和score数组中
}
//冒泡排序
for(i = 1; i < n; i ++)
{
for(j = 1; j <= n - i; j ++)
{
//比较第j个学生和第j + 1个学生的分数,如果第j + 1个学生分数高则交换位置(排名)
if(score[j] < score[j + 1])
{
//交换学号位置(排名)
int x = id[j + 1];
id[j + 1] = id[j];
id[j] = x;
//交换成绩位置(排名)
x = score[j + 1];
score[j + 1] = score[j];
score[j] = x;
}
}
}
//按排名从高到低输出学生信息(注意换行)
for(i = 1; i <= n; i ++)
{
printf("%d %d\n", id[i], score[i]);
}
return 0;
}