计数排序
计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:
统计相同元素出现次数
根据统计的结果将序列回收到原来的序列中
思路:
例如:
{6,1,2,9,4,2,4,1,4}
共9
个数
- 统计相同元素出现次数
6
出现1
次,1
出现2
次,2
出现2
次,9
出现1
次,4
出现3
次
- 根据统计的结果将序列回收到原来的序列中
定义一个i来遍历,i表示数组内元素
i
先到0
的位置,里面没有数据,不打印然后到
1
的位置,里面有2
,打印2
次依次类推
最后打印结果:
1 1 2 2 4 4 4 6 9
新数组的大小怎么确定?
利用原数组中最大的值来确定。创建
max+1
个空间的数组。但这就会出现问题:比如有负数怎么办?比较
{3, 4, 10000}
呢?如果把负数变成正数,取绝对值呢?也不行。因为正负数绝对值一样的情况下无法区分正负数。
那如果我们把负数统一加一个正数,让他变成整数呢?
我们可以让
count=max-min+1
对于
{-5,-5,9,5,1}
count=9-(-5)+1=15
创建数组,数组大小为
15
,下标为0-14
-5
放在数组下标为0
的位置中,这个位置元素为2
依此类推,下标
-5
就是原来的元素,这些元素和下标形成了映射关系。
代码:
Sort.h
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<time.h>
#include <assert.h>
#include <stdbool.h>
#include <memory.h>
//打印
void PrintArr(int* arr, int n);
//计数排序
void CountSort(int* arr, int n);
Sort.c
#include "Sort.h"
//打印
void PrintArr(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
//计数排序
void CountSort(int* arr, int n)
{
//根据最大值最小值确定数组大小
int max = arr[0], min = arr[0];
for (int i = 1; i < n; i++)
{
if (arr[i] > max)
{
max = arr[i];
}
if (arr[i] < min)
{
min = arr[i];
}
}
int range = max - min + 1;//确定数组元素个数
int* count = (int*)malloc(sizeof(int) * range);//创建数组
//判断不为空
if (count == NULL)
{
perror("malloc fail!");
exit(1);
}
//初始化count数组中所有的数据为0
memset(count, 0, range * sizeof(int));//count的大小是range
//例如{100,101,109,105,101,105}
//min = 100,arr[i] - min就是count里面的下标
//统计数组中每个数据出现的次数
for (int i = 0; i < n; i++)
{
count[arr[i] - min]++;//这里的++是为了把次数放进去
}
//取count中的数据,往arr中放
int index = 0;
for (int i = 0; i < range; i++)
{
while (count[i]--)//这里是为了把上面count对应次数给传进arr
{
arr[index++] = i + min;//表示arr数组的下标从0开始,存放数据
}
}
}
test.c
#include "Sort.h"
int main()
{
int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };
int n = sizeof(a) / sizeof(int);
printf("排序前:");
PrintArr(a, n);
CountSort(a, n);
printf("排序后:");
PrintArr(a, n);
return 0;
}
计数排序的特性:
计数排序在
数据范围集中
时,效率很高,但是适用范围及场景有限。 而且只能适用于整数排序,无法对小数排序。时间复杂度:
O(N + range)
空间复杂度:
O(range)
稳定性:
稳定