本次的三种排序算法,时间复杂度都为O(n)
桶排序
桶排序,是根据待排序数据,先划分桶,然后将数据分到各个桶中,在桶中完成排序后,再依次取出后则完成了排序。
桶排序的时间复杂度是O(n),我们来分析一下。假设有一个待排序数组,元素个数为n,划分m个桶,每个桶内使用时间复杂度为O(klogk)的快速排序进行排序,此时桶排序的时间复杂度为m * k * logk,由于k=n/m,所以m * n/m * logn/m = nlogn/m,当桶的个数 m 接近数据个数 n 时,log(n/m) 就是一个非常小的常量,这个时候桶排序的时间复杂度接近 O(n)。
但桶排序对数据的要求比较高,需要排序数据能尽可能均匀的分布到各个桶里,负责在极端情况下,时间复杂度会退化到O(nlogn)。
正常情况下: 按前面的推导公式,可以得出时间复杂度接近O(n)。
极端情况下: 由于数据都分布到了一个桶,相当于快速排序,所以会导致时间复杂度退化到O(nlogn)。
桶排序适合做外部排序,比如有1GB的数据需要排序,但内存只有100M,这时用桶排序算法时,可以将1GB的数据分为10个文件,可能由于数据分布不均匀某些文件超过100M,则将超过100M的文件再次拆分为多个桶,直至拆分的文件最终不超过100M,然后对每个桶分别进行排序。
计数排序
计数排序也有分桶的概念,这里分桶是根据待排序元素最大值决定的,如果待排序数组最大值为k,则分为k+1个桶。每个桶内的元素都是相同的,所以桶内不需要排序。
排序过程:
- 先分桶,然后对每个元素在对应的桶内计数
- 分桶计数完成后,从左至右依次相加
- 进行排序(难点)
计数排序的排序过程是最难理解的,我们通过一张图来分解过程,排序的顺序是从待排序数组的尾部开始的
计数排序对排序数据也有特殊要求:
- 不能为负数,如果有负数,需要将负数转为正数再排序,比如:最小值为-100,则需要对数组整体+100然后再排序
- 必须为整数,如果有小数位,也需要转换为整数,再分桶计数
- 排序最大值不能过大,否则不适合分桶计数,比如身份证号排序,分桶数为18位数
基数排序
上面提到身份证号不适合用计数排序,这里我们就可以用到基数排序了。
基数排序也用到桶的概念,是对待排序元素的每一位值排序,排序过程中可以分为0-9这样10个桶,先从最低位开始排序,依次放入对应的桶然后按顺序取出,直至最高位完成排序。根据所有待排序元素中位数最高的元素来决定需要循环的次数。比如:最小值3,和最大值12345678,则3的其它为由0填充为00000003,每一位进行一次排序,则需要排序8轮。
另外,不只限于用数字分桶,也可以是单个英文字母,但分桶范围不能过大。
时间复杂度:对于一个长度为n的数组,单位数排序一轮的时间复杂度为O(n),如果最长的一位数长度为k,则需要进行k轮排序,复杂度为O(n * k),k不能太大,一般可以看做一个常数忽略不计,所以基数排序的时间复杂度近似为O(n)。
从其它地方找到一个比较形象的动图
总结
| 排序算法 | 基于桶的方式 | 数据要求 |
|---|---|---|
| 桶排序 | 基于数字范围分配桶 | 数据分布均匀,保证数据可以均匀的分配到所有桶中 |
| 计数排序 | 基于单个元素分配桶 | 必须是正整数,元素最大值不能过大 |
| 基数排序 | 基于每一位数字分配桶 | 数据可以分割出独立‘位’进行比较,独立‘位’的范围不能太大 |
桶排序、计数排序、基数排序都是时间复杂度为O(n)的排序算法,都是基于桶的概念。排序效率高,但都对数据有一定的要求,所以应用不是很广泛。
所以在合适的场景下,选择适合的排序方法,能很大的提高排序效率。