假设数组中有10个整数,取值范围为0~10,要求用最快的速度把这10个整数从小到大进行排序。可以根据这有限的范围,建立一个长度为11的数组。数组下标从0到10,元素初始值全为0。
一、定义
计数排序,这种排序算法是利用数组下标来确定元素的正确位置的。
二、思路
假设数组中有10个整数,取值范围为0~10,要求用最快的速度把这10个整数从小到大进行排序。
可以根据这有限的范围,建立一个长度为11的数组。数组下标从0到10,元素初始值全为0。
假设数组数据为:9,1,2,7,8,1,3,6,5,3 。
下面就开始遍历这个无序的随机数列,每一个整数按照其值对号入座,
同时,对应数组下标的元素进行加1操作。
例如第1个整数是9,那么数组下标为9的元素加1。
最终,当数列遍历完毕时,数组的状态如下:
该数组中每一个下标位置的值代表数列中对应整数出现的次数。
直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次,0不输出。
则顺序输出是:1、1、2、3、3、5、6、7、8、9。
计数排序:计数排序只能用在数据范围不大的场景中,如果数据范围k比要排序的数据n大很多,就不适合用计数排序了。
而且,计数排序只能给非负整数排序,如果要排序的数据是其他类型的,要将其在不改变相对大小的情况下,转化为非负整数。
如果起始数不是从0开始,为了不浪费空间,可以采用偏移量的方式解决。
比如,如果考生最低成绩0分,最高900分,但成绩要精确到小数后一位,我们就需要将所有的分数都先乘以10,转化成整数,然后再放到9010个桶内。
比如,如果要排序的数据中有负数,数据的范围是[-1000, 1000],那我们就需要先对每个数据都加1000,转化成非负整数。
比如,分数排序: 95,94,91,98,99,90,99,93,91,92 ,数组起始数为90,这样数组前面的位置就浪费了。可以采用偏移量的方式:
三、代码实现
import java.util.Arrays;
public class CountingSort{
public static void main(String[] args) throws Exception {
int[] scores = {9, 3, 4, 9, 1, 2, 7, 8,1,3, 3, 4, 0, 10, 9, 7, 9};
for (int n : sort(scores)) {
System.out.println(n);
}
}
public static int[] sort(int[] sourceArray) throws Exception {
// 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
//
int maxValue = getMaxValue(arr);
return countingSort(arr, maxValue);
}
private static int[] countingSort(int[] arr, int maxValue) {
int bucketLen = maxValue + 1;
int[] bucket = new int[bucketLen];
for (int value : arr) {
bucket[value]++;
}
int sortedIndex = 0;
for (int j = 0; j < bucketLen; j++) {
while (bucket[j] > 0) {
arr[sortedIndex++] = j;
bucket[j]--;
}
}
return arr;
}
private static int getMaxValue(int[] arr) {
int maxValue = arr[0];
for (int value : arr) {
if (maxValue < value) {
maxValue = value;
}
}
return maxValue;
}
}
四、复杂度
时间复杂度是O(n+m) n: 数据个数 m: 数据范围;
空间复杂度是O(m);
稳定性:稳定排序。