计数排序(Counting Sort)是一个非基于比较的排序算法,该算法的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。它的优势在于在对一定范围内的整数排序时,它的复杂度为 O ( n + k ) Ο(n+k) O(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法,而且当 O ( k ) > O ( n ∗ l o g ( n ) ) O(k)>O(n*log(n)) O(k)>O(n∗log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是 O ( n ∗ l o g ( n ) ) O(n*log(n)) O(n∗log(n)),如归并排序、堆排序)。
算法的步骤如下:
(1)找出待排序的数组中最大和最小的元素;
(2)统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
(3)对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
(4)反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
计数排序算法的基本思想:把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数,例如 temp[i] = m, 表示元素 i 一共出现了 m 次。最后再把临时数组统计的数据从小到大汇总起来,此时汇总起来是数据是有序的。
例如: 使用计数排序算法将数组 { 2,3,8,7,1,7,3,9,8,2,1,4,2,4,6,9 } 进行升序排序。
实现代码如下所示。
#include<iostream>
using namespace std;
void CountingSort(int *arr, int size)
{
int minValue = arr[0]; //通过maxValue和minValue计算出临时数组所需要开辟的空间大小
int maxValue = arr[0];
int* tmp = 0;
int count = 0;
for (int i = 0; i < size; i++) //找出待排序的数组中最大和最小的元素
{
if (arr[i] < minValue)
minValue = arr[i];
if (arr[i] > maxValue)
maxValue = arr[i];
}
int range = maxValue - minValue + 1; //计算数据的分散空间
tmp = (int*)malloc(sizeof(arr[0])*size); //申请辅助空间
if (tmp == NULL)
return;
memset(tmp, 0, sizeof(int)*range); //初始化
for (int i = 0; i < size; i++) //统计每个元素出现的次数
tmp[arr[i] - minValue]++; //在存储上要在原始数组数值上减去minValue才不会出现越界问题
for (int i = 0; i < range; i++) //通过统计tmp[];回收元素
{
while (tmp[i]--)
arr[count++] = i + minValue; //要将i的值加上minValue才能还原到原始数据
}
free(tmp);
}
int main()
{
int a[] = { 2,3,8,7,1,7,3,9,8,2,1,4,2,4,6,9 };
int len = (int) sizeof(a) / sizeof(*a);
cout << "排序前:" << endl;
for (int i = 0; i < len; i++)
{
cout << a[i] << " ";
}
cout << endl;
CountingSort(a, len);
cout << "排序后:" << endl;
for (int i = 0; i < len; i++)
{
cout << a[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
排序前:
2 3 8 7 1 7 3 9 8 2 1 4 2 4 6 9
排序后:
1 1 2 2 2 3 3 4 4 6 7 7 8 8 9 9
时间复杂度: O ( n + k ) O(n+k) O(n+k)。
空间复杂度: O ( k ) O(k) O(k)。
稳定性: 由于在排序的过程中,具有相同值的元素在输出数组中的相对次序与它们在输入数组中的相对次序相同。也就是说,对两个相同的数来说,在输人数组中先出现的数,在输出数组中也位于前面。所以计数排序是稳定的排序。