计数排序(Counting Sort)
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
算法描述
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
例子
数组: 2 3 4 8 7 6 4 3 2 2 1
找到最大最小值:8 1
设置一个计数数组: 对应1 2 3 4 5 6 7 8的空数组
循环数组:
count: 1 3 2 2 0 1 1 1
结果: 1 2 2 2 3 3 4 4 6 7 8
结束
算法复杂度瓬
空间复杂度: O(n+k)
时间复杂度:
排序不稳定,原来相等的两个参数排在前面的可能排在后面。
代码实现
public static void sort(int[] sourceArray) throws Exception {
// 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
int maxValue = getMaxValue(arr);
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]++;
}
System.out.println(Arrays.toString(bucket));
int sortedIndex = 0;
for (int j = 0; j < bucketLen; j++) {
while (bucket[j] > 0) {
arr[sortedIndex++] = j;
bucket[j]--;
}
}
System.out.println(Arrays.toString(arr));
return arr;
}
private static int getMaxValue(int[] arr) {
int maxValue = arr[0];
for (int value : arr) {
if (maxValue < value) {
maxValue = value;
}
}
return maxValue;
}
运行结果
输入数组: int[] arr = {2,3,4,8,7,6,4,3,2,2,1};
[0, 1, 3, 2, 2, 0, 1, 1, 1]
[1, 2, 2, 2, 3, 3, 4, 4, 6, 7, 8]
但是按照上面的步骤,这时候我们可以看到和演算结果大致相同,不过代码上只寻找了最大值,这里可以进行部分优化,减少内存的消耗。
public static void sort(int[] sourceArray) throws Exception {
// 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
int maxValue = arr[0];
int minValue = arr[0];
for (int value : arr) {
if (maxValue < value) {
maxValue = value;
}else if(minValue >value ){
minValue = value;
}
}
countingSort(arr, maxValue,minValue);
}
private static int[] countingSort(int[] arr, int maxValue, int minValue) {
int bucketLen = maxValue- minValue + 1;
int[] bucket = new int[bucketLen];
for (int value : arr) {
bucket[value-minValue]++;
}
System.out.println(Arrays.toString(bucket));
int sortedIndex = 0;
for (int j = 0; j < bucketLen; j++) {
while (bucket[j] > 0) {
arr[sortedIndex++] = j+minValue;
bucket[j]--;
}
}
System.out.println(Arrays.toString(arr));
return arr;
}
运行结果
输入数组: int[] arr = {2,3,4,8,7,6,4,3,2,2,1};
[0, 1, 3, 2, 2, 0, 1, 1, 1]
[1, 2, 2, 2, 3, 3, 4, 4, 6, 7, 8]
这样我们按时算法本身的描述进行操作,找到对应的最大最小值,然后按照最大最小值差设置新的存储数组,结果就和演算形式一样,这里大小数值不大看不出具体效果,当数据够大时候,就能压缩出一定的空间,不过计数算法毕竟是牺牲空间来换取时间。