本文内容和代码来自《漫画算法》
之前练习的冒泡排序、鸡尾酒排序、快速排序、堆排序都是基于元素比较和位置元素交换实现的,有一些特殊的排序并不基于元素比较,如计数排序、桶排序、基数排序。
以计数排序来说,这种排序算法是利用数组下标来确定元素的正确位置的。
来看一个例子:
假设有20个随机整数,取值范围0-10,要求用最快的速度把这20个整数从小到大进行排序
建立一个长度为11的数组,下标0-10,元素全为0。
假设20个数字如下所示
9,3,5,4,9,1,2,7,8,1,3,6,5,3,4,0,10,9,7,9
下面遍历这个20个元素的数字序列,每一个整数按其值对号入座,同时数组下标的元素进行加1操作。
例如第一个整数是9,那么下标为9的元素加1。
第二个整数是3,那么下标为3的元素加1。
继续遍历并修改数组…
最终当数列修改完毕,数组的状态如下。
该数组的每一个下标位置的值代表数列中对应整数出现的次数。有了这个统计结果,排序就很简单了。直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次。
0 1 1 2 3 3 3 4 4 5 5 6 7 7 8 9 9 9 9 10
显然,现在输出的数列已经是有序的了。
这就是计数排序的基本过程,它适用于一定范围内的整数排序。在取值范围不是很大的情况下,它的性能甚至快过那些时间复杂度为O(nlogn)的排序。
后面还有很多图片,就不比着书画了。最主要的还有当有重复数据的时候涉及到的一个顺序的处理。还是看一下原书,更好理解一点。除了重复数据顺序的问题,还有一个就是无需序列不从0开始的情况的处理。
最终的java实现如下
int[] countSort(int[] arr) {
int max = 0, min = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
int d = max - min;
int[] countArr = new int[d + 1];
for (int i = 0; i < arr.length; i++) {
countArr[arr[i] - min]++;
}
for (int i = 1; i < countArr.length; i++) {
countArr[i] += countArr[i - 1];
}
int[] sortedArr = new int[arr.length];
for (int i = arr.length - 1; i >= 0; i--) {
sortedArr[countArr[arr[i] - min] - 1] = arr[i];
countArr[arr[i] - min]--;
}
return sortedArr;
}
@Test
public void fun1() {
int[] arr = new int[] { 49, 38, 65, 97, 76, 13, 27, 49 };
int[] sortedArr = countSort(arr);
System.out.println(Arrays.toString(sortedArr));
}
操作原始数组运算量n,操作统计数列运算量为m
时间复杂度O(n+m)
不考虑结果数组和统计数组,
空间复杂度O(m)
它的时间复杂度是线性级的,然而它的局限性也非常严重
1.当数列最大和最小值差距过大时,并不适合用计数排序
例如给出20个随机整数,范围在0到1亿之间,需要创建1亿个元素的数组,不但严重浪费空间,而且时间复杂度也会随之升高。
2.当数列元素不是整数时,也不适合用计数排序
如果数列中的元素都是小数,如25.213,或0.00 000 001这样的数字,则无法创建对应的统计数组。这样显然无法进行计数排序。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)