计数排序(Counting sort)是一种稳定的线性时间排序算法。
当输入的元素是 n个min到max之间的整数时候,k = max - min +1,它的时间复杂度是O(n+k)。
计数排序不是比较排序,排序的速度快于任何比较排序算法。当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(nlog(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(nlog(n)),如归并排序,堆排序)
例如:{0,-1,3,2,7,0,1,2,1},这里min = -1,max = 7,那么k = max - min + 1 = 9,需要一个大小为k的辅助数组。统计[min,max]之间数字出现的次数.
步骤:
- 找到待排序数组的最大值和最小值;
- 统计数组中每个数字出现的次数,存入辅助数组中;
- 对辅助数组总数累计(后面的值出现的位置为前面所有值出现的次数之和);
- 逆序输出辅助数组中的值。
代码:
#include <iostream>
#include <vector>
using namespace std;
void countSort(vector<int>& arr)
{
int len = arr.size();
if(len == 0)
return;
//这里需要一个原始的数组拷贝
vector<int> tempArr(arr.begin(),arr.end());
//查找min,max
int min = tempArr[0],max = min;
for(int i=1;i<len;++i)
{
if(min>tempArr[i])
min = tempArr[i];
if(max<tempArr[i])
max = tempArr[i];
}
//计算k值
const int k = max-min+1;
int count[k]={0};
for(int i=0;i<len;++i)
++count[tempArr[i]-min];//这里需要一个偏移量min,因为数组是从下标0开始的,统计次数
for(int i=1;i<k;++i)
count[i] +=count[i-1];//后面的键值出现的位置为前面所有键值出现的次数之和,也就是每个数所在的范围
//这里是逆序排序,这个和我们统计每个数字出现的位置有关系,
//比如0出现了3次,加上之前-1出现的次数,就是4。
//我们倒序排序第一个key值为0的时候,那么在count中的大小应该是4,那么它在arr数组的下标就该是4-1,count自减1为3
//排序第二个key值为0的时候,count为3,在arr数组的下标是3-1
for(int i=len-1;i>=0;--i)
arr[--count[tempArr[i] - min]] = tempArr[i];
//这里--count[tempArr[i] - min]很精简
//又解决了下标需要减1的问题,对应的count数组中的值也减1
}
int main()
{
vector<int> arr{-1,3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4,
7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
countSort(arr);
for(auto value:arr)
cout<<value<<" ";
cout<<endl;
return 0;
}
输出结果: