目录
1. 原理
首先看一个题目,有n个数,取值范围是 0~n,写出一个排序算法,要求时间复杂度和空间复杂度都是O(n)的。
为了达到这种效果,这一篇将会介绍一种不基于比较的排序方法。这种方法被称为计数排序。
计数排序的思路是这样的,对于每一个待排序元素a,如果知道了待排序数组中有多少个比它小的数,那么就可以直接知道在排序后的数组中 a 应该在什么位置上。比如,如果一个数组中有3个数是比a小的,那么,在排序后的数组里,a必然会出现在第4位。
现在问题转化成,对于待排序数组里的一个数,如何能快速知道比它小的数字有多少个。要解决这个问题,我们不能使用比较的办法,那样时间复杂度是无法降下来,只有换一个思路,以空间换时间。因为n个数的取值范围是 0~n,所以,不妨使用一个大小为 n 的数组来统计从0到n,每个数在待排序数组中出现的次数。这个数组类似于直方图数组,因为这种方式也被称做是基于统计的排序。直方图统计的思路简单清晰,在很多题目中都会有出现,一定要熟练掌握这种技巧。
强调:计数排序适合排序一组集中的数据。
2. 代码实现
//计数排序
public static void countSort(int[] array) {
//1. 找到待排序数组的范围,也就是找到最大值和最小值
int max = array[0];
int min = array[0];
//循环遍历找寻最小值和最大值
for (int i = 1; i < array.length; i++) {
if (array[i] > max)
max = array[i];
if (array[i] < min)
min = array[i];
}
//计算待排数组的长度
int len = max - min + 1;
//2. 定义一个计数数组
int[] count = new int[len];
//3. 遍历array数组,把数据计数到计数数组中
for (int i = 0; i < array.length; i++) {
count[array[i] - min]++;
}
//4. 将array数组还原
int index = 0;//来控制array数组的下标
for (int i = 0; i < array.length; i++) {
//这个循环的作用,是把count里面标记的数据取出来
while (count[i] > 0) {
array[index] = i + min;
index++;
count[i]--;
}
}
}
public static void main(String[] args) {
int[] a = {5,4,3,2,1};
Sort.countSort(a);
for (int x : a) {
System.out.print(x + " ");
}
}
3. 性能分析
时间复杂度 | 空间复杂度 |
O(MAN(N,范围)) | O(N) |
对数据的范围敏感 | 对数据的范围敏感 |