非比较排序

913 字
5 分钟
非比较排序

非比较排序#

比较类排序有理论下界 O(n log n),非比较排序通过不比较元素大小来突破这个下界。代表算法有基数排序计数排序

基数排序#

核心思想#

  • 非比较排序:不比较关键字大小,而是按关键字的每一”位”做分配与收集
  • 多关键字排序:将单个关键字拆分为 d 位,每位看作一个子关键字
  • 借助稳定的”分配-收集”:对每一位使用桶(队列)完成一趟排序,d 趟后整体有序

算法步骤#

每一趟包含两个步骤:

  1. 分配:扫描序列,根据当前位的值将元素放入对应的桶(队列 Q₀~Q_{r-1})
  2. 收集:按桶编号 0→r-1 依次将各桶元素取出,串接成新序列

重复 d 趟(从最低位到最高位),排序完成。

LSD vs MSD#

方式处理顺序特点
LSD从最低位到最高位实现简单,408 考试默认方式
MSD从最高位到最低位需递归对每个桶内部排序,较复杂

排序过程示例#

序列 {278, 109, 063, 930, 589, 184, 505, 269, 008, 083}(LSD, d=3, r=10):

趟次按位收集结果
第 1 趟个位930, 063, 083, 184, 505, 278, 008, 109, 589, 269
第 2 趟十位505, 008, 109, 930, 063, 269, 278, 083, 184, 589
第 3 趟百位008, 063, 083, 109, 184, 269, 278, 505, 589, 930

复杂度#

指标
时间O(d(n+r))
空间O(r)
稳定性稳定

⚠️ 易错:基数排序有 LSD 和 MSD 两种。408 默认考 LSD——从最低位开始排,要求每趟分配使用的排序算法是稳定的

计数排序#

核心思想#

对每个元素 x,统计比 x 小的元素有多少个,就能直接确定 x 在排序结果中的位置。

算法步骤#

  1. 计数:创建计数数组 C[0..k],统计每个值出现的次数
  2. 累加:对 C 做前缀和——C[i] 表示值 ≤ i 的元素总个数
  3. 输出从后往前扫描 A,根据 C 确定每个元素的最终位置

代码实现#

void CountingSort(int A[], int B[], int n, int k) {
int C[k + 1];
memset(C, 0, sizeof(C));
for (int i = 0; i < n; i++) // 计数
C[A[i]]++;
for (int i = 1; i <= k; i++) // 前缀和
C[i] += C[i - 1];
for (int i = n - 1; i >= 0; i--) { // 从后往前保证稳定
B[C[A[i]] - 1] = A[i];
C[A[i]]--;
}
}

复杂度#

指标
时间O(n+k)
空间O(n+k)
稳定性稳定
适用条件整数且取值范围 k 不太大

⚠️ 易错:计数排序的稳定性依赖于第 3 步从后往前扫描。如果从前往后,相同值的元素会被反序放置。

⚠️ 易错:计数排序只适合顺序存储,因为需要根据计数值直接定位下标。

考研高频考点#

  • ⭐ 基数排序是非比较排序,不受 O(n log n) 下界限制
  • ⭐ 时间复杂度 O(d(n+r)) 中各参数的含义及计算
  • ⭐ LSD 分配与收集过程手动模拟(大题)
  • ⭐ 计数排序 O(n+k)
  • ⭐ 从后往前扫描保证稳定性的原理
  • 计数排序与基数排序的关系(基数排序每趟底层可用计数排序实现)
  • 适用场景:关键字位数少、记录数量多

关联页面#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

非比较排序
https://lingluoa.icu/posts/数据结构/sorting-non-comparison/
作者
lingluoa
发布于
2026-07-07
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
lingluoa
Hello, I'm lingluoa.
公告
欢迎来到我的博客!不定期更新中。
文章目录
标签
站点统计
文章
53
分类
9
标签
75
总字数
175,771
运行时长
0
最后活动
0 天前
站点信息
构建平台
ESA Pages
博客版本
Firefly v6.15.6
文章许可
CC BY-NC-SA 4.0