这个问题是在联系中科大的导师时被导师问到的,那个时候已经通过了中科大的免试研究生的面试,然后联系了一个导师,得知我人在合肥时让我去他办公室聊聊,其实大概就是了解下专业水平吧。问了很多问题,这是其中一个。虽然距离现在已经过去了好几个月,但是突然想到了这个问题,就想把它写下来。
下面来说说这个问题吧,忽略内存的问题,假设这些考试成绩是可以一次被装载进内存的。由于数据比较多,哪怕是利用快速排序算法,性能也不是很好。其实这个时候我们可以好好分析下问题,被排序的对象是分数,分析下分数的特征,分数都是整数而且范围都在 0-100 之间。其实这个时候就应该要联想到桶排序了,下面来解决下这个问题。
其实我们这里只需借助一个一维数组就可以解决这个问题。请确定你真的仔细想过再往下看哦。
首先我们需要申请一个大小为 101 的数组 int a[101]。OK 现在你已经有了 101 个变量,编号从 a[0]~a[100]。刚开始的时候,我们将 a[0]~a[100] 都初始化为 0,表示这些分数还都没有人得过。例如 a[0] 等于 0 就表示目前还没有人得过 0 分,同理 a[1]等于 0 就表示目前还没有人得过 1 分……a[100]等于 0 就表示目前还没有人得过 100 分。
下面我们开始遍历所有人的分数,例如第一个人得了80分,那么我们令 a[80]++,如果第二个得了90分,那么我们令 [90]++,…,依次遍历当一个人得了 i 分之后我们就令 a[i]++。
最后你发现没有,a[0]~a[100]中的数值其实就是 0 分到 100 分每个分数出现的次数。接下来,我们只需要将出现过的分数打印出来就可以了,出现几次就打印几次,a[0]为 0,表示“0”没有出现过,不打印。我们可以写出下面的程序:
1 |
|
这样基本只要一次遍历就可以完成排序了,时间复杂度为 O(n),比一般的排序算法快很多。我们进一步思考,如果我们要输出的不仅仅是分数还有学生信息呢,比如说从小到大输出所有学生的姓名和分数,那么这个时候也只要做一点小小的变动就好了。
我们用一个结构体去表示一个学生,如下:
1 | typedef struct students{ |
然后把所有学生信息放在一个链表中去存储。
接下来考虑排序过程中处理的问题,这个时候排序的数组就不能简单的用 int a[101] 了,我们这时候就要用一个 student 的指针数组 student * score[101] 。
排序的过程中,我们依次遍历所有的学生分数,假设某个学生的分数为 i ,那么就在 score[i] 的后面插入该学生的节点:
1 | unsigned int s = q->score; |
遍历完所有的学生分数后,我们就会发现整个学生信息的存储结构就会发生一个很大的变化,之前是所有的学生节点连成一个链表。但是现在排序完后,所有相同分数的学生信息放在一个链表的里,这些链表的头节点又都放在数组里,数组的下标就是他们的分数。
完整的代码如下:
1 |
|
测试输入:
7
a 20
b 30
d 10
c 20
e 5
f 5
g 40
测试输出:
排序之前:
a:20
b:30
d:10
c:20
e:5
f:5
g:40
排序之后:
f:5
e:5
d:10
c:20
a:20
b:30
g:40