在所有基于比较的排序算法中,最快的算法其时间复杂度为O(NlogN),事实上可以证明,这已经是基于比较的排序算法的时间复杂度下界了。但有一种实现简单的非比较排序算法可以突破基于比较的排序算法这一时间下界——计数排序(Counting Sort)。
如果我们的待排序序列为整数且有一个已知的数值范围K,那么计数排序可以在O(N+K)的时间复杂度内将序列排序好。在多数情况下计数排序的效率比任何基于比较的排序算法都要高,除非O(K)>O(NlogN)。计数排序是一种以空间换时间的算法。
计数排序的基本思想是对每一个数,统计其他小于这个数的个数,如此一来就可以确定这个数在排序后所在的位置。对于存在重复元素的情况,我们需要先对每一个重复元素进行统计。
const int k = 100; //元素范围
void CountingSort(vector<int> &vec)
{
vector<int> count(k, 0);
//统计重复元素
for (const int &i : vec)
{
++count.at(i);
}
//统计比i小的元素的个数以确定排序后i所在的位置
for (size_t i = 1; i < vec.size(); ++i)
{
count.at(i) += count.at(i-1);
}
//将元素一个个放入对应位置
vector<int> aux = vec;
for (size_t i = 0; i < vec.size(); ++i)
{
vec[--count[aux[i]]] = aux[i];
//vec.at(--count.at(aux.at(i))) = aux.at(i);
}
}
一般情况下计数排序的效率都非常高,但其仅局限于整数排序且已知元素值范围的情况,比如对一个数据量非常大但其元素范围相对很小的应用场景——如上千万考生按成绩排序(假设成绩都为整数且范围是[0, 100])。 此外,计数排序的核心思想也是基数排序的重要组成部分。