计数排序适合整数范围小、重复多的场景;不支持负数、浮点数、字符串等非整类型,需先校验极值并偏移下标,空输入和边界须显式处理,k
计数排序适合什么场景?
计数排序不是万能的,它只在整数范围小、重复多时才真正快。比如对
[]int{1, 2, 5, 3, 2, 1, 4}排序,最大值是 5,最小值是 1,范围仅 5;但如果数组里有999999或负数很多(如-1000000),count数组会极大,内存直接爆掉。
- 范围判断必须做:先算
maxValue和minValue,再用maxValue - minValue + 1算长度,不能默认从 0 开始- 不支持浮点、字符串、结构体等非整类型,强行转 int 容易溢出或截断
- 如果输入全是唯一的大整数(如
[1000001, 1000002, 1000003]),计数排序比sort.Ints还慢,因为要分配百万级切片为什么原版实现常 panic 或结果错?
最常见两个坑:没处理负数、没做边界校验。
count[value] += 1会 panic:当value是负数时,索引越界- 没减
minValue就直接当数组下标用,等于把负数映射到非法内存地址- 累加阶段写成
for i := 1; i 没问题,但若 <code>count长度为 0(空输入)就会 panic,得先判len(arr) == 0正确做法是:
- 先检查空切片:
if len(arr) == 0 { return arr }- 找极值时遍历一次,别只看
array[0]就初始化minValue,否则全负数时会出错- 所有访问
count的地方都用arr[i] - minValue做偏移,不裸用arr[i]稳定版和非稳定版怎么选?
稳定版保证相同元素的相对顺序不变,比如
[2a, 1, 2b]排完是[1, 2a, 2b];非稳定版可能是[1, 2b, 2a]。
- 如果你只是排纯数字且不关心“哪个 2 先出现”,用简单循环重建法更直观:
for i := 0; i 0 { result[index] = i + minValue index++ count[i]-- } }- 如果后续要扩展为基数排序,或数据带附属字段(比如
struct{val int; id string}),必须用稳定版:从原数组末尾开始填,靠累加后的count定位位置,并立刻count[val]--性能对比和替代方案
计数排序理论是
O(n + k),其中k是值域宽度。但实际中:
k 时,通常比 <code>sort.Ints快 2–3 倍(实测 10 万内 0–99 整数)k > 10*n时,内存占用和缓存失效让它比quickSort慢一个数量级- Go 标准库的
sort.Ints是优化过的 introsort(快排+堆排+插排混合),通用性远强于计数排序所以真实项目里:
- 别为了“学算法”硬套计数排序
- 真遇到高频小范围整数(比如 HTTP 状态码统计、像素灰度值排序),再手写
- 否则直接用
sort.Slice(nums, func(i, j int) bool { return nums[i] 更安全值域大、数据杂、又想快?那该考虑桶排序或基数排序,而不是在这儿调
count数组大小。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!












