计数排序

基本思想

如果我们能够得到数组中元素在排序后的位置就好了。计数排序就是基于这种愿望。

基本步骤

  1. 准备一个辅助数组用来记录某个数组元素的个数。cnt[i]=j 意味着,大小为 i 的元素有 j 个;
  2. 计算 cnt 数组的前缀和数组 ss[i-1]=j 意味着比 i 小的数的个数为 j;
  3. 上一步骤中的 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 值不变,不会影响其他数的最终位置。