计数排序算法
计数排序算法
算法简介
计数排序是一个分布式排序。分布式排序是使用已经组织好的辅助数据结构(称为 “桶”),来得到排好序的数组。它是用来排序整数的优秀算法(它是一个整数排序算法),但是需要更多的内存来存放临时的数组。
算法分析
它主要的流程就是先通过原数组得到一个计数数组,然后通过计数数组恢复出排好序的数组:原数组——>计数数组——>排好序的数组。至于计数数组怎么得到,以及计数数组怎么恢复出原数组的问题,看看下面的例子就知道了。
假设有一个比较常规的数组(数组的最小值是 0,且数组中的每一项都是整数,且数值分布在 0~N 的区间内),常规的计数排序过程是这样的:
- 因为数组的下标是天然排好序的(从小到大),所以我们进行计数数组的下标与原数组的值进行关联 (这个关联的规律自己心里知道就行)。最简单的关联就是,计数数组下标是 0,则其对应了原数组中的数组值 0。那么此时,计数数组下标的最大值就对应了原数组中的数值的最大值。
- 找出原数组中的最大值 N,然后创建长度为 N + 1 的计数数组。比如原数组中的最大值为 20,那么计数数组的下标的最大值也要为 20(即数组下标范围:0 ~ 20),那么数组的长度就是 20 + 1 = 21。
- 创建完计数数组后,进行计数数组的生成,统计原数组中的值出现的个数。比如原数组中的 2 出现了 3 次,1 出现了 4 次,那么就要在计数数组下标为 2 的地方记一个 3,在下标为 1 的地方记一个 4。
- 计数数组生成完了就能还原出排好序的数组。计数数组的下标记录的是原数组中出现的值,计数数组的值记录的是这个原数组中的值出现的次数,比如计数数组为 [3, 4, 1],那么它恢复出的排好序的数组就是 [0, 0, 0, 1, 1, 1, 1, 2]。
问题与优化
上面的步骤会存在一点点问题:
- 如果出现了负整数该咋整?
- 如果数组中的最大值为 650,但是整个数组中的值在 600 ~ 650 这个区间内,那么如果创建长度为 651 的数组,其中有 600 长度不是浪费了吗?
这一点点问题的解决方法是,改变计数数组的下标与原数组中的值的关联就行了。也就是对上面的 步骤 1 做出调整。具体的关联方法就是,求出数组中的最大值 max 和 最小值 min,计数数组的下标 0 不再对应原数组中的值 0,而是对应原数组中的最小值 min。计数数组的长度即为:max - min + 1;计数数组的下标与原数组中的值的关联关系为:index + min = value,其中 index 为 计数数组的下标,min 为原数组中的最小值,value 为当前计数数组下标对应的原数组里面的值。
图解
通过上面的图解可以发现,计数排序适用在数组的范围不那么大的情况,如果数组的范围超级大,那么所需要的计数数组就要超级长,那么内存空间的消耗就是不容乐观的。
代码示例
1 | function countingSort(arr, maxValue) { |
复杂度
时间复杂度 O(n+k)
空间复杂度 O(k)
稳定性 稳定
评论
LivereValine