计数排序算法思想
- 动图演示:
- 基本思想:
计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。用到的是哈希映射的思想。
- 操作步骤:
- 统计相同元素出现次数。
- 根据统计的结果将序列回收到原来的序列中。
假设现在要对如下数组进行排序:
计数的核心步骤就是数组中数字的值是多少,就把该值映射到新开辟数组的下标上。仔细观察这串数组,最大数字为10,所以这里我们要开辟11个空间,才能保证数字10放到新数组下标为10的位置,遍历原数组,一个val出现几次,它映射的位置++几次。
上述方法即哈希的绝对映射思想,原数组的某个元素是几,对应新开辟数组下标为几的位置就相应的加加,出现几次,加加几次。此方法看似可行,但存在一个大问题,空间复杂度过大,如若一组数据为【10000,9999,5000,9999,5000,8888】,难道说为了排这几个数字,你要开辟10001个大小空间的数组吗,得不偿失,更何况你这10001个空间的前5000个空间没有值映射,纯纯浪费了空间,这就是绝对映射的弊端,因此要用相对映射。
- 绝对映射:原数组是几,映射到新数组下标位置++。
- 相对映射:此时新数组下标的范围是从0到原数组最小的值,而映射到下标的位置为原数组val的值 - 原数组最小min的值。
综上,计数排序使用于数据有一些重复,数据范围比较集中 ,从而避免空间浪费过于严重。
代码如下:
//计数排序
void CountSort(int* a, int n)
{
int min = a[0], max = a[0];
//先求出原数组的最大和最小值
for (int i = 1; i < n; i++)
{
if (a[i] < min)
min = a[i];
if (a[i] > max)
max = a[i];
}
//求出新数组的范围
int range = max - min + 1;
//开辟新数组
int* countA = (int*)malloc(sizeof(int) * range);
assert(countA);
//把新开辟数组初始化为0
memset(countA, 0, sizeof(int) * range);
//计数
for (int i = 0; i < n; i++)
{
countA[a[i] - min]++; //统计相同元素出现次数(相对映射)
}
//排序
int j = 0;
for (int i = 0; i < range; i++)
{
while (countA[i]--)
{
a[j++] = i + min; //赋值时,记得加回原先的min
}
}
free(countA);
}
计数排序特性总结
- 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
- 时间复杂度:O(N + range)。
- 空间复杂度:O(range)。
- 稳定性:稳定。