【硬核】小明高考考了680分,他想知道在全国1000W考生中排什么名次?

作者: Tom哥
简介:计算机研究生,校招进阿里,期间还拿过百度、华为、中兴、腾讯等6家大厂offer,P7 技术专家。出过专利,CSDN博客专家。
公众号:微观技术,分享其他地方看不到的知识与思考,欢迎关注

有这么一道题:

今年高考,有1000万的考生参加了考试,满分750,小明靠了680分,他想知道自己的全国排名,如何快速排序呢?

这里面会给大家介绍几种算法:

【硬核】小明高考考了680分,他想知道在全国1000W考生中排什么名次?_第1张图片

一、桶排序

算法思想

将要排序的数据拆分、分组放入几个有序的桶里,然后分别对每一个桶中的元素排序,最后将桶中的元素依次取出,就完成了最终的排序。

例子:

对 9,8,19,2,7,15,20,6,4,1,11,17 等数字排序。(为了简化描述,这里只列举了12个数)

其中,最小值是 1,最大值是 20。整个区间最大跨度是 20,我们将其分成了4个桶,然后再采用快速排序对每个桶里的元素排序。

【硬核】小明高考考了680分,他想知道在全国1000W考生中排什么名次?_第2张图片

如果待排序的数据是m个,均匀的分到n个桶中,每个桶中的元素个数 j=m/n

每个桶采用快速排序,时间复杂度是 O(j*log(j)),所有桶的时间复杂度是 O(n*j*log(j))

整理后,该算法的时间复杂度是 O(m*log(m/n))

如果桶个数n无限接近m,那桶排序的时间复杂度近似 O(m)

适用场景

1、数据均匀分布,没有严重倾斜。否则,很容易发生大部分数据集中在某几个桶中

2、桶容易划分,如:手机号排序就不太适合

3、桶与桶之间天然有序,不需要再单独排序

4、一些特殊的场景,比如数据文件很大,有几十个G,内存无法一次全部加载,可以考虑分桶,分批排序

如果桶中的数据分布不均匀怎么办?

【硬核】小明高考考了680分,他想知道在全国1000W考生中排什么名次?_第3张图片

一图胜千言,“拆”字万里行,大事化小,小事化了。

我们对原始数据分组选桶时,可以为每个桶设定一个计数器,当发现某个分桶的数据量偏大时,可以考虑将该桶二次拆分为若干子桶。

当然,如果子桶的数据量还是很大,我们可以进一步拆分为子子桶。

拆分的深度,可以结合具体业务情况,自己把控。

如果排序的数据的范围不大(比如人的年龄、考试成绩),采用先分桶再快速排序有些浪费,我们可以采用下面这种排序方式。

二、计数排序

计数排序的要求是排序的数据范围不大,比如有m个数,其中最大值是i,那么可以分成i个桶,每个桶里的数据都是相同的,这样就省掉了对桶内元素的排序。

举个典型的场景:

我们都参加过高考,今年有1000万的考生参加考试,满分750,小明考了680分,他想知道自己的全国排名,如何快速排序呢?

【硬核】小明高考考了680分,他想知道在全国1000W考生中排什么名次?_第4张图片

满分750,考生的分数最小可能是0分,最高是750分,所以我们就分为了 751 个桶,按分数将考生放入对应的桶中。

然后,再依次读取每个桶中的数据,写入一个数组中,便得到了 1000万考生的分数排名。

小明考了680分,他想快速知道自己的排名,如何实现?

方案一:遍历排序好的数组,由于是由大到小且有序的,我们找到第一个680的元素,便得到最终的排名。

方案二:对算法进行优化,每个桶配备一个计数器,桶中每添加一个元素,计数器加一。这样,你只需要将681~750 之间的桶的计数器累计求和,便得到最终的排名。

三、基数排序

上面的两种排序,还是很好理解的,可以解决很多业务场景问题。

但如果是对若干数量的手机号由小到大排序,怎么解决呢?

我们知道,手机号是11位,范围太大,而桶排序和计数排序,对数据范围有较高要求,显然手机号不太合适。

这里介绍一种新的排序算法,基数排序。

核心思想

先比较高位的值,如果a的高位大于b的高位,那么a肯定是大于b的,后面的低位值就可以不用比较。

比如:对下面的若干英文名做排序

【硬核】小明高考考了680分,他想知道在全国1000W考生中排什么名次?_第5张图片

解题思路,如上图所示

首先,对每个名称的第一个字母做排序,可以采用分桶或计数排序。

同一个桶内的元素,然后提取第二个字母,再次分桶或计数排序,

循环遍历,直到比较完第11位,

当然,比较期间,如果某个阶段,桶中的元素只有一个,那么该阶段可以终止。

有点类似上面的《如果桶中的数据分布不均匀怎么办?》解决思路。

特别注意:

上面排序的英文名字长度可能不同,我们先要做数据预处理,取最大的长度,将位数不够的后面补"0"。

因为ASCII 值,最小的是字母A,对应的十进制是65,也是大于0。所以,补“0” 不影响排序。

适用场景

如果整体比较难度较大,数值的区间较大,不适合分桶。我们可以考虑采用分割的思想,分割出若干小段,依次对小段来比较,各个小段存在递进关系。


关于我:Tom哥,前阿里P7技术专家,出过专利,竞赛拿过奖,CSDN博客专家,负责过电商交易、社区生鲜、互联网金融等业务,多年团队管理经验。

你可能感兴趣的:(java,算法,leetcode)