「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」
什么是计数算法
不清楚---拿来看下官方的定义
当输入的元素是n 到 k 之间的整数时,它的运行时间是O(n + k)。计数排序不是比较排序,因此不被 Ω(nlogn)的下界限制。
由于用来计数的数组C 的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序算法中,能够更有效的排序数据范围很大的数组。
通俗地理解,例如有10个年龄不同的人,统计出有8个人的年龄比A小,那A的年龄就排在第9位,用这个方法可以得到其他每个人的位置,也就排好了序。当然,年龄有重复时需要特殊处理(保证稳定性),这就是为什么最后要反向填充目标数组,以及将每个数字的统计减去1。
算法的步骤如下:
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 对所有的计数累加C 中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素i放在新数组的第i项,每放一个元素就将i减去1
分析
上面一大波文字解释了什么叫做计数算法
自己来提取分析下
- 计数排序和常用的通过交换位置的排序不一样
- 计数排序是通过获取数组中最大最小值,然后构建一最小值 和 最大值 区间的数组 原数组的值必定是最大值 和 最小值中间的 3.通过构建的数组记录(计数)原数组中出现的值的次数 4.最后通过数值出现的次数吧数值一次取出来
代码
第一步:获取最大最小值
var min = Math.min.apply(null, arr)
var max = Math.max.apply(null, arr)
第二步: 构建一个计数的数组
因为要记录最小到最大最小值, 所以长度钥匙 max - min + 1
var counter = Array.from({ length: max - min + 1 }).fill(0) // 给个初始的0 代表max min 之间值出现的次数
第三步:开始计数
// 遍历数组
for(let i = 0; i < arr.length;i++) {
var counterIndex = arr[i] // 获取arr i项 的值
counter[counterIndex]++ // i项的值 出现次数 +1
}
第四部: 取出计数
遍历counter计数的数组 把counter的下标一次取出给到新数组
var ret = []
for(let j = 0; j < counter.length; j++) {
// 当前j 是arr的值 counter[j]记录了j出现的次数
// 把j按次数取出
while(counter[j] > 0) {
ret.push(j)
counter[j]--
}
}
// ret 就是我们的值
结果-有问题 。。。 哪里出错了?
第一次做的时候是这个样子
我再思考了下
第二步骤里 计数数组的长度 是 max - min + 1
而我们第三步 计数的时候 值是有问题 我取的是arr[j]项的值去做counter 技术数组的下标 这样没法把 counter下标和 原数组的值对应起来, 所以我们要保证counter的下标的准确性(>=0),这样我们要把arr第i项 + min 这样就只可以对应counter下标 相应的取值的时候那么要把min 加上
最终的代码是
var counterSort = function (arr) {
var min = Math.min.apply(null, arr)
var max = Math.max.apply(null, arr)
var counter = Array.from({ length: max - min + 1 }).fill(0)
for (var i = 0; i < arr.length; i++) {
var value = arr[i]
var counterIndex = value - min
counter[counterIndex]++
}
var ret = []
for (var i = 0; i < counter.length; i++) {
while (counter[i] > 0) {
ret.push(j + min)
counter[j]--
}
}
return ret
}
结尾
因为对数组排序 需要对改变原数组 最后一步里我们对arr进行重新赋值
var index = 0
for (var i = 0; i < counter.length; i++) {
while (counter[i] > 0) {
arr[index++] = j + min
counter[j]--
}
}
return arr
总结
计数排序和普通排序不一样 不是通过对比交换位置,是一个全新的思路
另外我们也可以使用Map来计数
var counterSort = function (arr) {
var min = Math.min.apply(null, arr)
var max = Math.max.apply(null, arr)
var counter = new Map()
for (let i = min; i <= max; i++) {
counter.set(i, 0)
}
for (var i = 0; i < arr.length; i++) {
var value = arr[i]
var counterIndex = value
counter[counterIndex]++
counter.set(counterIndex, counter.get(counterIndex) + 1)
}
var index = 0
for (j of counter) {
while (j[1] > 0) {
arr[index++] = j[0]
j[1]--
}
}
return arr
}
也可以稍微改动一下写法
var counterSort = function (arr) {
const max = Math.max.apply(null, arr)
const min = Math.min.apply(null, arr)
const counter = Array.from({ length: max - min + 1 }).fill(0)
for (let i = 0; i < arr.length; i++) {
const count = arr[i] // 这里就去当前值做
if (counter[count] === undefined) counter[count] = 0 // 然后塞入一个值
counter[count]++ // +1
}
let index = 0
// 这里就可以从min开始遍历
for (let i = min; i < counter.length; i++) {
while (counter[i] > 0) {
arr[index++] = i
counter[i]--
}
}
return arr
}