概念
以数组元素值为键,出现次数为值存进一个临时数组,最后再遍历这个临时数组还原回原数组。因为 JavaScript的数组下标是以字符串形式存储的,所以计数排序可以用来排列负数,但不可以排列小数。
复杂度
最好:O(n + k),k是最大值和最小值的差。
最坏:O(n + k)
平均:O(n + k)
分类
常规计数排序
代码
function counting_sort(nums) {
let arr = []
let leg = nums.length;
let max = Math.max(...nums)
let min = Math.min(...nums)
for (let i = 0; i < leg; i++) {
let temp = nums[i]
arr[temp] = arr[temp] + 1 || 1
}
let index = 0
for (let i = min; i < max; i++) {
while (arr[i] > 0) {
nums[index++] = i
arr[i]--
}
}
}
var sortArr = [9, 6, 3, 5, 2, 1, 7, 343, 6, 643, 243, 544, 5, 63, 234, 0, 56, 123]
counting_sort(sortArr)
console.log(sortArr)
// [0, 1, 2, 3, 5, 5, 6, 6, 7, 9, 56, 63, 123, 234, 243, 343, 544, 123]
缺点
优化计数排序
把每一个数组元素都加上 min 的相反数,来避免特殊情况下的空间浪费,通过这种优化可以把所开的空间大小从 max+1 降低为 max-min+1,max 和 min 分别为数组中的最大值和最小值。
比如数组 [103, 102, 101, 100],普通的计数排序需要开一个长度为 104 的数组,而且前面 100 个值都是 undefined,使用该优化方法后可以只开一个长度为 4 的数组。
function counting_sort(nums) {
let arr = []
let leg = nums.length;
let max = Math.max(...nums)
let min = Math.min(...nums)
let mark = 0
let add = -min
// 加上最小值的相反数来缩小数组范围
for (let i = 0, len = nums.length; i < len; i++) {
mark += 1
let temp = nums[i];
temp += add;
arr[temp] = arr[temp] + 1 || 1;
}
console.log(arr)
let index = 0
for (let i = min; i <= max; i++) {
let temp = arr[i + add];
mark += 1
while (temp > 0) {
mark += 1
nums[index++] = i;
temp--;
}
}
console.log(mark)
}
var sortArr = [9, 6, 3, 5, 2, 1, 7, 343, 6, 643, 243, 544, 5, 63, 234, 0, 56, 123, 9999]
counting_sort(sortArr)
console.log(sortArr)
// 10038
// [0, 1, 2, 3, 5, 5, 6, 6, 7, 9, 56, 63, 123, 234, 243, 343, 544, 643, 9999]
使用感受
就计数排序而言,最好数的最大最小差异不是很大,如果比较值本身差异很大,创建的数据空间就会越大,遍历数也会变多, 而且这个优化的计数排序如果数是正负都有的 那个 空间大小从 max+1 降低为 max-min+1 这个就会变成相加的,反而会让性能变差,而且这种优化感觉没什么大的用处