理解:
根据原数据中相等数出现的次数进行排序,原数据的每一个值对应了桶中的每个索引,索引指向的值就是该索引出现的次数,再依次将数据从桶中弹出,即可完成排序
代码:
// 计数排序 根据数组中相同数据出现的次数来排序
// 转换之后数组中的数据会变成桶的索引, 桶中每个索引指向的值就表示该索引出现的次数
function countSort(arr) {
// 特殊数据处理
if (arr == null || arr.length < 2) {
return
}
// 取得数组中的最大值
let max = arr[0]
for (let i = 1; i < arr.length; i++) {
max = Math.max(max, arr[i])
}
// 根据最大值开辟一个辅助空间 桶bucket
// 为了使得桶的索引值能够取到最大值 桶的长度为max+1
let bucket = new Array(max + 1)
// 在桶每个位置上开辟一个空间 用来存放原数组中的数据 这样写是为了保持稳定性
// [ [], [], [], [] ]
for (let i = 0; i < bucket.length; i++) {
bucket[i] = []
}
// 将数组中的数据插入到桶中对应索引的位置上
// [ [0...0], [1...1], [2...2], [3...3] ]
for (let i = 0; i < arr.length; i++) {
// 数据从尾部插入到每个位置 保持稳定性
bucket[arr[i]].push(arr[i])
}
// 此时桶中的数据已经有序 并且相等数据的相对位置保持不变
// 原数组索引
let j = 0
// 遍历桶的每个位置
for (let i = 0; i < bucket.length; i++) {
// 当桶中每个位置上还有数据时继续循环
while (bucket[i].length > 0) {
// 将桶每个位置上的数据覆盖到原数组中
// 每个位置上的数据从头开始弹出 保持稳定性
arr[j++] = bucket[i].shift()
}
}
}
性能:
- 时间复杂度:O(N)
- 空间复杂度:O(M),辅助空间桶bucket
- 稳定性:原数据依次存入桶中,再从桶中依次弹出,可以保持稳定性
局限性:
- 原数据必须有范围:由于是根据数据最大值开辟辅助空间桶bucket,所以数据必须有范围,且不宜过大。
- 原数据可能需要特殊调整:桶bucket的索引值表示原数据的值,索引指向的数据表示该索引出现的次数。由于索引是从0开始,如果原数据中出现负数或者不从0开始,原始数据需要做特殊处理,使其与桶的索引相互对应。