21.计数排序:不用比较的“空间换时间”排序算法

一、什么是计数排序?

计数排序(Counting Sort)是一种非比较排序算法 ,它的核心思想是通过统计每个元素出现的次数,直接生成有序数组,不需要进行任何元素之间的比较。

简单来说,计数排序就像"按号码排队":

  • 你有一堆号码牌,号码从1到8;
  • 准备9个桶(0到8),每个桶对应一个号码;
  • 把每个号码牌放到对应的桶里,统计每个桶里有多少个号码;
  • 从小到大依次把桶里的号码拿出来,就得到了有序的序列。

二、计数排序的核心步骤

计数排序的核心步骤可以分为以下三步:

  1. 准备桶 :先找出数组中的最大值,创建一个大小为最大值+1的桶数组,初始值全为0;
  2. 计数:遍历数组,每个元素对应桶的位置加1,统计每个元素出现的次数;
  3. 输出:从小到大遍历桶数组,把每个桶里的元素逐个取出,生成有序数组。

三、计数排序的代码实现

1. C++版本

cpp 复制代码
#include <vector>
#include <algorithm>
using namespace std;
vector<int> counting_sort(vector<int>& arr) {
if (arr.empty()) return {};
// 1. 准备桶:找出最大值,创建桶数组
int max_val = *max_element(arr.begin(), arr.end());
vector&lt;int&gt; count(max_val + 1, 0);
// 2. 计数:统计每个元素出现的次数
for (int num : arr) {
count[num]++;
}
// 3. 输出:从小到大生成有序数组
vector&lt;int&gt; sorted_arr;
for (int i = 0; i &lt; count.size(); i++) {
for (int j = 0; j &lt; count[i]; j++) {
sorted_arr.push_back(i);
}
}
return sorted_arr;
}

2. Python版本

python 复制代码
def counting_sort(arr):
    if not arr:
        return []
# 1. 准备桶:找出最大值,创建桶数组
max_val = max(arr)
count = [0] * (max_val + 1)
2. 计数:统计每个元素出现的次数
for num in arr:
count[num] += 1
3. 输出:从小到大生成有序数组
sorted_arr = []
for i in range(len(count)):
sorted_arr.extend([i] * count[i])
return sorted_arr
测试
arr = [3, 5, 2, 8, 2, 5, 1, 3]
print("排序前:", arr)
sorted_arr = counting_sort(arr)
print("排序后:", sorted_arr)  # 输出:[1, 2, 2, 3, 3, 5, 5, 8]

3. C语言版本

c 复制代码
#include <stdio.h>
#include <stdlib.h>
// 找出数组中的最大值
int find_max(int arr[], int n) {
int max = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
// 计数排序
void counting_sort(int arr[], int n) {
if (n <= 0) return;
// 1. 准备桶:找出最大值,创建桶数组
int max_val = find_max(arr, n);
int* count = (int*)malloc((max_val + 1) * sizeof(int));
for (int i = 0; i &lt;= max_val; i++) {
    count[i] = 0;
}
// 2. 计数:统计每个元素出现的次数
for (int i = 0; i &lt; n; i++) {
count[arr[i]]++;
}
// 3. 输出:从小到大生成有序数组
int index = 0;
for (int i = 0; i &lt;= max_val; i++) {
for (int j = 0; j &lt; count[i]; j++) {
arr[index++] = i;
}
}
free(count);
}
// 打印数组
void print_array(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {3, 5, 2, 8, 2, 5, 1, 3};
int n = sizeof(arr) / sizeof(arr[0]);
printf("排序前:");
print_array(arr, n);
counting_sort(arr, n);
printf("排序后:");
print_array(arr, n);  // 输出:1 2 2 3 3 5 5 8
return 0;
}

四、计数排序的特点

  • 时间复杂度:O(n + k),其中n是元素个数,k是最大值;
  • 空间复杂度:O(k),需要额外的桶数组;
  • 是否比较:不需要进行任何元素之间的比较;
  • 适用场景:元素范围较小的整数数组,如考试分数、年龄、号码牌等;
  • 稳定性:稳定,相同元素的相对位置保持不变。

五、计数排序的优化

为了提高计数排序的效率,可以进行以下优化:

  1. 偏移优化:如果数组中的元素不是从0开始,可以计算偏移量,减少桶的大小;
  2. 稳定优化:通过前缀和的方式,保持相同元素的相对位置;
  3. 多轮计数:对于多位数的排序,可以使用计数排序作为基数排序的子过程。

六、计数排序的实际应用场景

计数排序是一种高效的排序算法,常见场景包括:

  1. 考试分数排序:学生的考试分数通常在0到100之间,适合用计数排序;
  2. 年龄排序:人口统计中的年龄排序,年龄范围通常较小;
  3. 号码牌排序:活动中的号码牌排序,号码范围固定;
  4. 基数排序:作为基数排序的子过程,用于多位数的排序。

七、计数排序 vs 比较排序

计数排序和比较排序是两种不同的排序算法,它们的区别如下:

特性 计数排序 比较排序
是否比较
时间复杂度 O(n + k) O(n log n) 或 O(n²)
空间复杂度 O(k) O(1) 或 O(n)
适用场景 元素范围较小的整数 任意元素类型
稳定性 稳定 不一定

八、总结

计数排序是一种非比较排序算法,它通过统计每个元素出现的次数,直接生成有序数组,不需要进行任何元素之间的比较。

计数排序的时间复杂度为O(n + k),空间复杂度为O(k),适合元素范围较小的整数数组,是一种高效的排序算法。

希望这篇文章能帮助你理解计数排序的原理和实现!

相关推荐
智码看视界1 小时前
第4篇:循环神经网络及其变体 LSTM/GRU 实战
python·自然语言处理·gru·lstm·循环神经网络·情感分析·梯度消失
Web3&Basketball1 小时前
Agent外传审计实战:3类失准事故拦截脚本
python·大模型·agent·性能调优·推理优化
何以解忧,唯有..1 小时前
Milvus 向量数据库的 DDL、DML、DQL 操作详解
python
Rabitebla1 小时前
【Linux系统编程】 指令(二):一条路径是怎么定位到文件的
linux·c++·笔记·学习·算法
罗狮粉 991 小时前
AI-Gateway — 面向 AI Agent 的本地 Runtime Gateway
人工智能·python·inscode·ai编程
蓝胖的四次元口袋1 小时前
Python数据结构知识梳理
python
程序员清风1 小时前
生产级智能体平台设计:任务编排、工具管理与运行监控
人工智能·python·aigc
库玛西1 小时前
数据结构与算法之美:树、森林与二叉树全解析
c语言·数据结构·笔记·考研·算法·学习方法
木井巳1 小时前
【记忆化搜索】最长递增子序列
java·算法·leetcode·深度优先·剪枝·推荐算法