一、程序思想及步骤
(一)思想
计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。
(二)步骤
- 通过遍历原数组,数组中每个数的值是多少就对统计次数的数组位++,从而统计出数组每个数出现的次数。
- 根据统计的结果将该数放回到原来的数组中
3.确定统计次数数组的范围,通过步骤1遍历原数组从而得到最大值和最小值,从而确定范围。
(三)演示
1.如下图,要对该数组进行计数排序,首先遍历该数组可知该数组范围为[0,5]
其中0出现了2次,1出现了0次,2出现了2次,3出现了3次,4出现了0次,5出现了1次。
2.通过次数排序所以可知
3.最后我们将该结果放回原数组中即可实现排序
(四)思想优化
当数组中存在2000、2500、2500、2564、2561、2850、3000数时我们如果在使用找到的最大值5000和最小值2000当做范围进行排序的话,会白白浪费很多空间,所以这里我们使用相对位置(数组中所有值减去数组中的最小值)作为范围,即会产生一组新的相对数据为0、500、500、564、561、850、1000.此时我们计数数组的区间范围为[0,1000].
二、代码实现
(一)遍历原数组,确定计数数组的相对数值区间范围。
(二)开辟一个新的计数数组空间
memset的作用是将range空间大小的数组所有值置为0,方便为之后的计数做好铺垫。
(三)统计原数组值个数并计入计数数组
arr[j]-min意思是:arr数组中的数值减去最小值得到的相对数值通过计数数组的某一位次数存储起来,假设arr[1]-min即2500-2000为500,那么计数数组中的第500位加1次,从而统计出2500出现了一次。
(四)将计数数组中的值存入原数组中,即为新的排序结果
countArr[K]–意思是计数数组中某一位出现多少次就循环该数组多少次,比如countArr[500]=2相当于2500这个数出现了两次,那么我们需要循环两次赋值给原数组,最后将计数数组中的数值赋给原数组,即可达到排序的目的。
(五)完整代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
// 计数排序
void CountSort(int* arr, int n)
{
assert(arr);
//遍历数组,确定计数数组的相对数值范围
int min = arr[0]; //将min和max值设为原数组第一个值
int max = arr[0];
for (int i = 1; i < n; i++) //从i=1开始是因为两两进行比较,i=0时的那个值已经给min和max了,所以从i=1开始
{
if (arr[i] < min)
min = arr[i]; //找出最小的值
if (arr[i] > max)
max = arr[i]; //找出最大的值
}
int range = max - min + 1; //+1 是因为假设区间为[0,7]则该区间一共有7-0+1个位置
//开辟一个计数数组空间
int* countArr = (int * ) malloc (sizeof(int)*range);
memset(countArr, 0, sizeof(int)*range); //将该计数数组所有位置值置为0
//统计原数组值个数并计入计数数组
for (int j = 0; j < n; j++)
{
countArr[arr[j] - min]++; //arr[j]-min意思是:arr数组中的数值减去最小值得到的相对数值通过计数数组的某一位次数存储起来
//假设arr[1]-min即2500-2000为500,那么计数数组中的第500位加1次,从而统计出2500出现了一次。
}
//将计数数组中的值存入原数组中,即为新的排序结果
int index = 0;
for (int k = 0; k < range; k++)
{
while (countArr[k]--) //countArr[K]--意思是计数数组中某一位出现多少次就循环该数组多少次
{ //比如countArr[500]=2相当于2500这个数出现了两次,那么我们需要循环两次赋值给原数组
arr[index++] = k + min; //将计数数组中的数值赋给原数组
}
}
free(countArr); //释放该计数数组
}
int main()
{
int arr[7] = { 2000,2500,2500,2564,2561,2850,3000 };
//int arr[7] = { 42,2,0,2,1,50,30 };
int n = 7;
CountSort(arr,n);
for (int i = 0; i < n; i++)
{
printf("%d ",arr[i]);
}
printf("\n ");
system("pause");
return 0;
}
三、总结
- 计数排序在数据范围集中时,效率很高,但是该算法只适用于整型,如果浮点数或者字符串排序,还得用比较排序。
- 时间复杂度:O(MAX(N,范围)) N和范围哪个大选取哪个
- 空间复杂度:O(范围)
- 稳定性:稳定