持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情
前言
提起排序算法那真是太多了,什么插入排序、快排、冒泡排序、堆排序等等,今天新了解了一种排序感觉还挺有意思的,那就是技术排序。
计数排序的核心
- 计数排序的核心就用一个新的数组来记录目标数组,通过一定的转换来实现排序
- 首先初始化一个中间数组,用来存储目标数组,这里目标数组的长度要设置成为目标数组的长度+1
- 中间数组的下标是目标数组的项,比如说目标数组中有两个2,两个3,那到中间数组中的表现形式就是在下标为2的地方值为2,下标为3的地方值为3 如下代码:
var hIndex = function (citations) {
let result = 0
let barrels = new Array(Math.max(...citations) + 1).fill(0)
//把数据插入计数数组
for (let i = 0; i < citations.length; i++) {
barrels[citations[i]]++
}
//这里是还原排序后的原来数组
let newIndex = 0
for (let j = 0; j<barrels.length; j++) {
//可能有重复的数字
while(barrels[j]>0){
citations[newIndex++] = j
barrels[j]--
}
}
return citations
};
console.log(hIndex([3,0,6,5,1]))
运行结果如下:
优化代码
1.上面的代码生成的中间数组的长度是最大值+1,试想一下如果目标数组是从200开始的,那前200个常熟是不是就浪费了?
2.如果数组中有负数怎么办?
基于上上面的两点可以把代码改为如下格式
function countingSort(nums) {
let max = Math.max(...nums); // 找到这个数组的 最大 最小值
let min = Math.min(...nums);
//创建计数数组,这里计数数组的长度改为了max-min+1,这样就会解决上面所说的问题1
let count = new Array(max - min + 1).fill(0)
for (let i = 0; i < nums.length; i++) {
// index + min = value
//这里的计数数组的下标不再是目标数组的项,而是用nums[i]-min来表示,这样不管是正数还是负数都可以在计数数组中标记出来
let index = nums[i] - min
count[index]++;
}
// 还原原数组
let sortedIndex = 0;
for (let i = 0; i < count.length; i++) {
while (count[i] > 0) {
nums[sortedIndex++] = i + min; // 关联关系为:index + min = value
count[i]--;
}
}
return nums;
}
结果如下:
总结
可以发现计数排序的实现很简单,并且时间复杂度也小,只有O(n),比其它所有的排序都低,但是不是你也和我一样以前没怎么听说过这个排序,这是因为它也有着很明显的缺点。那就是:
- 计数排序正能排序整数,如果是小数就不行
- 由于使用了新的数组,内存消耗比较大