Skip to content

算法Algorithm

计数排序

施工中 · 内容逐步修订

Counting sort

对有限整数键频数计数并按前缀位置输出的线性时间非比较排序。

形式陈述 ​

设输入含 n≥1 条常数大小的记录,并取整数 k≥0;空输入直接返回。计数排序适用于每个键是区间 {0,…,k} 中整数的记录。用数组 C[0..k] 统计频数,再计算前缀和 P[v]=∑u=0vC[u]。P[v] 是键不大于 v 的记录总数,也就是键 v 的输出区间右端(不含端点)。令 P[−1]=0,则该区间为 [P[v−1],P[v])。按输入逆序处理记录:键为 v 时先令 P[v]←P[v]−1,再把完整记录放入 B[P[v]]。在能以一个机器字表示键、以常数时间寻址计数数组、且计数 0,…,n 和所需地址均能装入机器字的Word-RAM上,确定性最坏时间为 Θ(n+k),额外空间为 Θ(n+k)(可按是否保留记录调整)。它不通过元素间比较排序,因此不受比较排序 Ω(nlog⁡n) 下界约束。若只输出键而不保留记录,可直接按计数重写。

直觉

键域小时,与其比较元素,不如用值域索引为每个可能键开桶:先统计每个键出现次数,再把计数累加成连续输出区间。它把时间从 nlog⁡n 改为 n+k,代价是依赖有限且可寻址的键范围 k。稳定版本的前缀计数不仅告诉“有多少”,还精确给出每个键在输出中的结束位置。

计数排序的计数、前缀和与稳定输出
例子与边界

图中把相同键的记录标成 2a,2b,输入为 [2a,0,2b,1]。频数 [1,1,2] 累加成 P=[1,2,4],所以键 2 占下标 2,3。逆序扫描时,1 先放入 B[1],2b 放入 B[3],0 放入 B[0],最后 2a 放入 B[2],结果是 [0,1,2a,2b]。较晚出现的同键记录先占右侧位置,因而原先的相对次序得以保留。

再看包含空桶的例子:

输入 [2,5,3,0,2,3,0,3]、值域 0…5 时,频数为 [2,0,2,3,0,1];累加后可把每个元素从右向左放入对应位置,得到稳定排序 [0,0,2,2,3,3,3,5]。

若仍使用递减的结束位置却从左向右扫描,2a 会先占最右槽,从而颠倒同键次序。也可以用各桶的起始位置配合正序扫描实现稳定性;扫描方向必须与指针含义匹配。若只排序键而不附带记录,则无须这一步稳定性保证。若 k≫n,例如键范围是 0…109 而只有几百个元素,直接分配桶既浪费空间又会比比较排序更差;可压缩坐标或使用稀疏映射,但表示方式会改变复杂度,压缩本身通常还需要排序。负键可整体平移,浮点、任意实数或对象则需先提取有限整数键。计数排序以比较排序作为复杂度边界的对照,但本身不属于比较模型。

推论与应用

数组存储频数,前缀和把频数变成位置。计数排序是小整数键、直方图与离散事件聚合的基础,也是 LSD 基数排序保持稳定性的关键子过程。更一般的整数排序会在 k 太大而不能直接开桶时改用分位、打包或递归字典结构;这些算法的界依赖字长 w 和允许的字操作,不能由 Θ(n+k) 简单删去 k 得到。

参考资料
  • Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein, Introduction to Algorithms, 4th ed., MIT Press, 2022,§8.2。

  • Donald E. Knuth, The Art of Computer Programming, Vol. 3: Sorting and Searching, 2nd ed., Addison-Wesley, 1998,Chs. 5–6。

  • Robert Sedgewick and Kevin Wayne, Algorithms, 4th ed. 配套课程,§5.1 Key-Indexed Counting Demo,展示频数、累计位置与稳定分配;其正序起点版本与本页逆序终点版本不同。

关系图谱7 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系