计数排序
基本思想
如果我们能够得到数组中元素在排序后的位置就好了。计数排序就是基于这种愿望。
基本步骤
- 准备一个辅助数组用来记录某个数组元素的个数。
cnt[i]=j意味着,大小为 i 的元素有 j 个; - 计算
cnt数组的前缀和数组s,s[i-1]=j意味着比 i 小的数的个数为 j; - 上一步骤中的 j,就决定了 i 元素在排序后的位置为
j+1(如果数组的下标从 1 开始);
代码实现
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int n;
int a[N], s[N], w[N];
void count_sort() {
// 统计元素个数
for (int i = 0; i < n; i++) s[a[i]]++;
// 计算前缀和
for (int i = 1; i < N; i++) s[i] += s[i - 1];
// 得出元素 i 的位置
for (int i = n - 1; i >= 0; i--) w[--s[a[i]]]
// 填入原数组
for (int i = 0; i < n; i++) a[i] = w[i];
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
count_sort();
for (int i = 0; i < n; i++) cout << a[i] << " ";
return 0;
}
解读
为什么 w[--s[a[i]]] = a[i],而不是 w[s[a[i]] - 1] = a[i]?
为什么要从后往前排序?
问题出现在数组元素重复的情况。假设数组的元素为 1,1,1,1,1,那么 s[1] = 5,这就会导致所有的1都处在4号位置上。故而,需要每安置一个 1,就 --s[1]。
由于后面并不再对 s 数组进行前缀和操作,故而其他位置的 s 值不变,不会影响其他数的最终位置。