一、桶排序(Bucket sort)
1、原理
将要排序的数据分到几个有序的桶里,每个桶里的数据再进行排序。桶内的数据排序完毕之后,再把桶里的数据依次取出,组成的序列就是有序的了。
2、原地排序?
属于原地排序算法。主要看桶内排序是否时原地排序算法,若选择插入排序,则桶排序就是属于原地排序,若选择了快速排序,则不属于原地排序。
3、稳定性算法?
不属于稳定性排序算法。因为根据我的代码实现,桶内数据会申请内存,故空间复杂度为O(n)。
4、时间复杂度
(1)最好情况
桶数量很多,并且数据均匀分布在各个桶内。
假设要排序的数据数量为 n,分配的桶的个数为 m,那么每个桶中的数据的数量为 k = n / m 。桶内采用快速排序算法,根据之前的推到,快排的时间复杂度为O(nlogn),准确说应该是
C是常数。将 k 代入公式中,得到每个桶的排序时间为
将 k 的计算公式带入上述公式中,得到如下
m 个桶一共需要的时间为
当 m 接近于 n 时,上述公式就变为如下
所以时间复杂度为 O(n) 。
(2)最坏情况
数据只分配到了一个桶,算法退化成快排,时间复杂度为 O(nlogn) 。
5、数据要求
- 要排序的数据要轻松地划分成 m 个桶,并且桶与桶之间要有天然的大小顺序。
- 数据在各个桶内的分布是均匀的。
6、适用场景:外部排序(数据保存在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中)
假设有10G的订单数据,如何按照订单金额从小到大进行排序?
步骤如下:
- 扫描一遍订单,确定金额的最小值和最大值,假设分别为 1 和 10k 。
- 将订单金额分成100个桶,编号分别为 01,02,03,……,那么第 1 个桶内的金额为 1 ~ 1000,第 2 个桶内的金额为 1001 ~ 2000,……
- 每个桶内的数据大概为100M,加载到内存中进行快排是没有问题的,将排序结果按照桶编号依次存入一个固定文件中,那么该文件中最后显示的结果就是最终的排序结果。
二、计数排序(Counting sort)
1、原理
当要排序的数据 n 非常大时,而数据所处的范围又较小,比如最小值和最大值分别为 1 和 k ,那么可以把数据分成 k 个桶,每个桶内的数据都是相同的,从而节省了桶内的排序时间。
实际上计数排序是桶排序一种特殊情况,即:对于桶排序,每个桶内的数据需要进行排序,而计数排序的桶内数据都是相同的,不需要排序。
2、原地排序?
不属于原地排序算法。根据代码的实现原理,需要使用计数数组,所以空间复杂度为 O(n) 。
3、稳定性算法?
属于稳定性排序算法。因为在将数据放入桶中时,保持原有数据顺序,再复原时按照顺序复原可以。
4、时间复杂度
根据算法的实现原理,只需要遍历一遍数据即可,时间复杂度为 O(n) 。
5、应用栗子
如何将某省100万的考生的高考成绩从小到大排序?
分数范围为 0 ~ 900,每一分都有大量考生重合。可以将分数分成 901 个桶,桶内的分数都是相同,只需要遍历数据, 将数据放入各个桶内即可。
6、适用范围
- 数据范围不大,即:数据量要远大于数据范围。
- 数据只能给非负整数进行排序
三、基数排序(Radix sort)
1、原理
将待排序的数列中的数拆分成“位”(十进制),对每一位进行稳定性排序(例如计数排序),直到所有的位都排序完成。
2、原地排序?
不属于原地排序算法。因为各个位排序时使用了线性排序算法。
3、稳定性排序?
属于稳定性排序。因为针对每一位的操作实际上都是稳定性排序,保证了相同数据的前后位置。
4、时间复杂度
因为对每一位的排序采用计数排序,时间复杂度为 O(n),假设最大的数据所在的位(十进制)总数为 k,则时间复杂度为O(kn),总体来说时间复杂度约等于 O(n) 。
5、应用栗子
如何对 10 万个手机号码你进行从小到大排序?
这里可以采用基数排序算法,将所有的手机号码拆成11个数字,从后向前一次进行计数排序,因为计数排序属于稳定性 性算法,这就保证了最终的排序结果是正确的。
6、适用范围
- 可以将待排序的数据拆成“位”来比较,“位”与“位”之间存在递进关系。
- 每一位的数据范围不能太大,这样可以使用线性排序算法来排序,例如计数排序。
四、总结
1、性质
算法种类 | 时间复杂度 | 空间复杂度 | 原地排序 | 稳定性排序 |
桶排序 | O(n) | O(n) | × | × |
计数排序 | O(n) | O(n) | × | × |
基数排序 | O(n) | O(n) | × | × |
2、源代码
参考:极客时间《数据结构与算法之美》王争
这门课真心推荐,内容很经典、栗子很形象,里面还包含了很多面试题目。真是居家旅行必备良药。
(SAW:Game Over!)