计数排序(Counting Sort)是一种非比较排序算法,它的基本思想是利用数组下标的有序性,将元素作为计数数组的相应下标,并在该下标位存储元素数量,最后根据计数数组填充待排序数组。
计数排序的具体步骤如下:
1、找出待排序数组中的最大值和最小值。
2、创建一个大小为 (最大值 - 最小值 + 1) 的计数数组,数组中每个元素表示待排序数组中值为 (最小值 + i) 的元素个数。
3、遍历待排序数组,统计每个值出现的次数,并将统计结果保存在计数数组中。
4、对计数数组进行前缀和操作,得到每个值在排序后数组中的下标位置。
5、遍历待排序数组,根据计数数组中的统计结果将每个元素放置到正确的位置上。
执行过程请移步文章末尾:执行过程。
计数排序有以下几个优点:
1、时间复杂度为 O(n + k),其中 n 是待排序数据的个数,k 是待排序数据的范围,因此在数据范围不大的情况下,计数排序的时间复杂度是非常优秀的。
2、计数排序是一种稳定排序算法,相同大小的元素在排序后仍然保持着它们之间的相对顺序。
3、由于计数排序不需要进行比较操作,因此它的时间复杂度与待排序数据本身的分布情况无关。
但是,计数排序也存在一些缺点:
1、计数排序只适用于数据范围比较小的情况,如果数据范围过大,计数数组会占用过多的内存空间。
2、计数排序只能用于非负整数的排序,如果待排序数据包含负数,需要先将数据整体加上一个固定值,再进行计数排序。
代码实现:
package com.algorithm.sort;
import java.util.Arrays;
/**
* @author LuoFei
* @className: CountingSort
* @projectName algorithm
* @description: TODO
* @date 2023/2/27 14:10
*/
public class CountingSort {
public static void countingSort(int[] arr) {
int len = arr.length;
if (len <= 1) {
System.out.println("数组长度小于2位,无需排序,直接返回");
return;
}
//确定数据范围
System.out.println("找到数组中的最大值与最小值");
int max = arr[0], min = arr[0];
for (int i = 0; i < len; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
System.out.println("最大值:"+max+", 最小值:"+min);
int range = max - min + 1;
//创建计数数组
int[] count = new int[range];
System.out.println("创建用于计数的数组,数组大小:"+(range));
System.out.println("遍历待排序数组,将待排序数组元素值减去最小值,结果作为计数数组下标,该下标计数数组值加1。");
for (int i = 0; i < arr.length; i++) {
System.out.println("arr["+i+"]="+arr[i]);
count[arr[i] - min]++;
System.out.println("计数数组下标: "+(arr[i]-min)+"的值加1,count["+(arr[i]-min)+"]="+count[arr[i] - min]);
}
System.out.println("计数数组:"+Arrays.toString(count));
System.out.println("----------------------------------------");
System.out.println("对计数数组进行前缀和操作, 得出计数数组下标在待排序数组中位置");
for (int i = 1; i < count.length; i++) { // 对计数数组进行前缀和操作
count[i] += count[i - 1];
}
System.out.println("新计数数组:"+Arrays.toString(count));
System.out.println("创建临时数组用于存放排序后数组");
int[] temp = new int[len];
System.out.println("对待排序数组倒序遍历");
for (int i = len - 1; i >= 0; i--) { // 将待排序数组中的元素放置到正确的位置上
System.out.println("从计数数组第一位开始查找元素arr["+i+"]: "+arr[i]+", 计数数组下标为:"+(arr[i] - min)+",在排序后数组中位置:"+(count[arr[i] - min]-1));
temp[count[arr[i] - min] - 1] = arr[i];
count[arr[i] - min]--;
System.out.println("计数数组count["+(arr[i]-min)+"]值减1");
System.out.println("新计数数组:"+Arrays.toString(count));
System.out.println("临时数组:"+Arrays.toString(temp));
}
System.out.println("将临时数组赋值原数组");
for (int i = 0; i < len; i++) { // 将排序后的数组赋值给原数组
arr[i] = temp[i];
}
}
public static void main(String[] args) {
int[] arr = {5, 2, 8, 3, 9, 1};
System.out.println("计数排序");
System.out.println("初始数组:"+ Arrays.toString(arr));
System.out.println("==============开始计数排序===============");
countingSort(arr);
System.out.println("==============计数排序完成===============");
System.out.println("最终数组"+Arrays.toString(arr));
}
}
执行过程:
计数排序
初始数组:[5, 2, 8, 3, 9, 1]
==============开始计数排序===============
找到数组中的最大值与最小值
最大值:9, 最小值:1
创建用于计数的数组,数组大小:9
遍历待排序数组,将待排序数组元素值减去最小值,结果作为计数数组下标,该下标计数数组值加1。
arr[0]=5
计数数组下标: 4的值加1,count[4]=1
arr[1]=2
计数数组下标: 1的值加1,count[1]=1
arr[2]=8
计数数组下标: 7的值加1,count[7]=1
arr[3]=3
计数数组下标: 2的值加1,count[2]=1
arr[4]=9
计数数组下标: 8的值加1,count[8]=1
arr[5]=1
计数数组下标: 0的值加1,count[0]=1
计数数组:[1, 1, 1, 0, 1, 0, 0, 1, 1]
----------------------------------------
对计数数组进行前缀和操作, 得出计数数组下标在待排序数组中位置
新计数数组:[1, 2, 3, 3, 4, 4, 4, 5, 6]
创建临时数组用于存放排序后数组
对待排序数组倒序遍历
从计数数组第一位开始查找元素arr[5]: 1, 计数数组下标为:0,在排序后数组中位置:0
计数数组count[0]值减1
新计数数组:[0, 2, 3, 3, 4, 4, 4, 5, 6]
临时数组:[1, 0, 0, 0, 0, 0]
从计数数组第一位开始查找元素arr[4]: 9, 计数数组下标为:8,在排序后数组中位置:5
计数数组count[8]值减1
新计数数组:[0, 2, 3, 3, 4, 4, 4, 5, 5]
临时数组:[1, 0, 0, 0, 0, 9]
从计数数组第一位开始查找元素arr[3]: 3, 计数数组下标为:2,在排序后数组中位置:2
计数数组count[2]值减1
新计数数组:[0, 2, 2, 3, 4, 4, 4, 5, 5]
临时数组:[1, 0, 3, 0, 0, 9]
从计数数组第一位开始查找元素arr[2]: 8, 计数数组下标为:7,在排序后数组中位置:4
计数数组count[7]值减1
新计数数组:[0, 2, 2, 3, 4, 4, 4, 4, 5]
临时数组:[1, 0, 3, 0, 8, 9]
从计数数组第一位开始查找元素arr[1]: 2, 计数数组下标为:1,在排序后数组中位置:1
计数数组count[1]值减1
新计数数组:[0, 1, 2, 3, 4, 4, 4, 4, 5]
临时数组:[1, 2, 3, 0, 8, 9]
从计数数组第一位开始查找元素arr[0]: 5, 计数数组下标为:4,在排序后数组中位置:3
计数数组count[4]值减1
新计数数组:[0, 1, 2, 3, 3, 4, 4, 4, 5]
临时数组:[1, 2, 3, 5, 8, 9]
将临时数组赋值原数组
==============计数排序完成===============
最终数组[1, 2, 3, 5, 8, 9]