计数排序:核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
我们大概讲一下算法的步骤。
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.6.0version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.16.14version>
dependency>
dependencies>
假设我们要排序的数据是:8, 10, 12, 9, 8, 12, 8
首先我们看看计数统计是什么一回事,计数统计就是把数组中每个元素出现的次数都记录下来,并且能够通过元素找到对应的次数。
那么计数数组变形又是干啥呢?计数数组变形是从第一项开始,每一项都等于它本身和前一项的和,这样做,得到的值的意思是当前值前面还有多个数字,比如arr[1]=4,表示当前值前面有4-1=3个数字;arr[2]=5,表示当前值前面有5-1=4个数字。
最后我们看下具体是怎么排序的,又我们的数组的值推导得到索引,然后从计数数组中找到应该要排的位置,最后插入到对应的数组中,这种方式也是一种稳定的排序方式。
具体的编码实现如下,下面的实现方式也是稳定排序的方式。
/**
* 计数排序
*
* @param arr
* @return
*/
public static void countingSort(int[] arr) {
if (arr.length == 0) {
return;
}
// 原数组拷贝一份
int[] copyArray = Arrays.copyOf(arr, arr.length);
// 初始化最大最小值
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
// 找出最小值和最大值
for (int num : copyArray) {
max = Math.max(max, num);
min = Math.min(min, num);
}
// 新开辟一个数组用于统计每个元素的个数(范围是:最大数-最小数+1)
int[] countArray = new int[max - min + 1];
// 增强for循环遍历
for (int num : copyArray) {
// 加上最小偏差是为了让最小值索引从0开始,同时可有节省空间,每出现一次数据就加1
// 真实值+偏差=索引值
countArray[num - min]++;
}
log.info("countArray的初始值:{}", countArray);
// 获取数组的长度
int length = countArray.length;
// 计数数组变形,新元素的值是前面元素累加之和的值
for (int i = 1; i < length; i++) {
countArray[i] = countArray[i] + countArray[i - 1];
}
log.info("countArray变形后的值:{}", countArray);
// 遍历拷贝数组中的元素,填充到原数组中去,从后往前遍历
for (int j = copyArray.length - 1; j >= 0; j--) {
// 数据对应计数数组的索引
int countIndex = copyArray[j] - min;
// 数组的索引获取(获取到的计数数组的值n就是表示当前数据前有n-1个数据,数组从0开始,故当前元素的索引就是n-1)
int index = countArray[countIndex] - 1;
// 数组中的值直接赋值给原数组
arr[index] = copyArray[j];
// 计数数组中,对应的统计值减1
countArray[countIndex]--;
log.info("countArray操作后的值:{}", countArray);
}
log.info("排列结果的值:{}", arr);
}
public static void main(String[] args) {
int[] arr = new int[]{8, 10, 12, 9, 8, 12, 8};
log.info("要排序的初始化数据:{}", arr);
//从小到大排序
countingSort(arr);
log.info("最后排序后的结果:{}", arr);
}
运行结果:
要排序的初始化数据:[8, 10, 12, 9, 8, 12, 8]
countArray的初始值:[3, 1, 1, 0, 2]
countArray变形后的值:[3, 4, 5, 5, 7]
countArray操作后的值:[2, 4, 5, 5, 7]
countArray操作后的值:[2, 4, 5, 5, 6]
countArray操作后的值:[1, 4, 5, 5, 6]
countArray操作后的值:[1, 3, 5, 5, 6]
countArray操作后的值:[1, 3, 5, 5, 5]
countArray操作后的值:[1, 3, 4, 5, 5]
countArray操作后的值:[0, 3, 4, 5, 5]
排列结果的值:[8, 8, 8, 9, 10, 12, 12]
最后排序后的结果:[8, 8, 8, 9, 10, 12, 12]