5-2 其他排序:计数排序
计数排序
计数排序是一种非比较排序算法。当输入值的范围相对于待排序元素的数量较小时,它尤其高效。
- 计数排序的基本思想是统计输入数组中每个不同元素出现的频率,并利用该信息将元素放置在正确的排序位置上。
- 当输入元素的范围较小且与数组大小相当时,这种方法效果很好。例如,对于输入 [1, 4, 0, 2, 1, 1],数组大小为 6,元素范围为 0 到 4。
- 如果输入数组的范围大于 n Log n,其中 n 是数组的大小,那么我们可以使用标准的基于比较的排序算法(如归并排序)对数组进行排序。
计数排序算法
- 声明一个大小为max(arr[])+1的计数数组cntArr[],并将其初始化为0。
- 遍历输入数组arr[] ,并将arr[]的每个元素映射为cntArr[]数组的索引,即,对0 <= i < N执行cntArr[arr[i]]++。
- 计算cntArr[]中每个索引处的前缀和。
- 创建一个大小为N的数组ans[]。
- 从数组arr[] 的末尾开始遍历,并更新ans[cntArr[arr[i]] - 1] = arr[i]。此外,更新 cntArr [arr[i]] = cntArr[arr[i]]--。
为什么要计算前缀和?
我们可以简单地统计所有元素出现的次数,然后逐个将它们放入输出数组中,但为了保证算法的稳定性,我们计算了前缀和。需要注意的是,在构建前缀和 cntArr[] 之后,我们从数组的右端开始遍历,以确保最后一个出现的元素移动到排序数组中最后一个正确的位置。
代码实现
#include <iostream>
#include <vector>
std::vector<int> countsort(std::vector<int>& arr)
{
int n = arr.size();
// find the maximum element
int maxval = 0;
for (int i = 0; i < n; i++)
{
maxval = std::max(maxval, arr[i]);
}
// create and initialize cntArr array
std::vector<int> cntArr(maxval + 1, 0);
// count frequency of each element
for (int i = 0; i < n; i++)
{
cntArr[arr[i]]++;
}
// compute prefix sum
for (int i = 1; i <= maxval; i++)
{
cntArr[i] += cntArr[i - 1];
}
// build output array
std::vector<int> ans(n);
for (int i = n - 1; i >= 0; i--)
{
ans[cntArr[arr[i]] - 1] = arr[i];
cntArr[arr[i]]--;
}
return ans;
}
int main()
{
std::vector<int> arr = {2, 5, 3, 0, 2, 3, 0, 3};
std::vector<int> ans = countsort(arr);
for (int x : ans)
{
std::cout << x << " ";
}
return 0;
}
输出:

计数排序的复杂度分析、优缺点、以及应用:
计数排序的复杂度分析:
时间复杂度:在所有情况下均为 O(N+M),其中N和M分别是inputArray[]和countArray[]的大小。
辅助空间: O(N+M),其中N和M分别是outputArray[]和countArray[]占用的空间。
计数排序的优势:
- 如果输入范围与输入数量处于同一数量级,则计数排序通常比所有基于比较的排序算法(如归并排序和快速排序)运行速度更快。
- 稳定算法
计数排序的缺点:
- 不适用于小数。
- 如果待排序的值范围非常大,则效率低下。
- 它不是原地排序算法,它需要额外的空间来对数组元素进行排序。
计数排序的应用:
对于数据范围有限的情况,这是一种常用的算法。例如,按成绩对学生进行排序,按时间、日期、月份、年份等对事件进行排序。
它被用作基数排序算法中的一个子程序。
桶排序利用计数排序的思想,将元素分成不同的桶。

浙公网安备 33010602011771号