之前的排序都是基于比较的排序。计数排序,包括后面桶排序和基数排序则不是。
计数排序,顾名思义,记录元素的出现次数来排序。指定一个新数组,数组索引用来记录存储的元素,数组数字记录出现元素的次数。
计数排序经过一定的改进可以是out-place,稳定的线性时间排序算法。该排序适用于序列分布比较集中的情况,并且边界已知且比较小,并且排序的元素必须是整数。如果序列分布过于稀疏,边界太大,则开辟新数组的空间占用会很大。
public class Main {
public static void main(String[] args) {
int[] arr = {3, 3, 5, 6, 2, 1};
System.out.print("排序前:");
arrPrint(arr);
int[] out = CountSort(arr);
System.out.print("排序后:");
arrPrint(out);
}
// 计数排序
//
// 初始化max和min为arr的第一个元素,第一个for循环反复取arr中的最大值赋给max,
// 反复取arr中的最小值赋给min,这样就得到arr中的最大最小值max,min。
// max-min+1的长度就是计数数组count的长度。
// 第二个for循环,将arr中元素的出现次数在count中计数,count的索引为 元素-min,
// count的元素表示对应arr元素的出现次数,(本实例得到的count为[1, 1, 2, 0, 1, 1])。
// 简单版本的计数排序索引直接用来存储arr元素,因为索引必须从0开始,
// 假如arr中的min是100,max是110,arr元素分布在100-110,
// 那开辟的count数组中0-99的位置都被浪费了。所以我们的count数组长度设为
// arr最大max到最小值min的长度max-min+1,arr元素对应的索引值即为 元素-min。
// 第三个for循环,遍历计数数组count中的元素,将前面的计数结果累加到后一位的计数结果中
// (本实例得到的count为[1, 2, 4, 4, 5, 6])。count的索引与arr元素对应关系不变,
// 此时count的元素表示小于等于对应arr元素的元素数量。比如实例arr中的2这个数字,
// 对应到count的索引为 2-min = 2-1 = 1,索引元素count[1]=2,所以小于等于arr中2这个数
// 的数字总共有 count[1]=2 个。
//
// 创建输出结果数组result,
// 第四个for循环,倒叙遍历待排序数组arr,遍历索引为j,遍历元素为arr[j],
// 那么对应到count的索引为arr[j]-min,索引对应元素为count[arr[j]-min],
// 那么根据count数组的含义,在arr中,小于等于arr[j]的元素总共有count[arr[j]-min]个,
// 不算上arr[j]等于arr[j]自己这种情况,那么小于等于arr[j]的元素除自己以外,
// 总共有count[arr[j]-min] - 1个。既然有 count[arr[j]-min] - 1 个数字小于等于arr[j],
// 那么数组排序的时候,arr[j]在数组里就应该排名第 count[arr[j]-min] - 1 位这个位置。
// 我们直接在result的count[arr[j]-min] - 1这个位置将arr[j]填入。
// 即result[count[arr[j] - min] - 1] = arr[j]。填入result之后,
// arr[j]在计数数组count的对应计数 count[arr[j]-min] 减1,j继续向左遍历,
// 如此循环反复,最后可将arr中的数按照它们对应的大小排序好。循环结束,返回result即可。
private static int[] CountSort(int[] arr) {
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (max < arr[i])
max = arr[i];
if (min > arr[i])
min = arr[i];
}
int[] count = new int[max - min + 1];
for (int num: arr) {
count[num - min]++;
}
for (int i = 1; i < count.length; i++) {
count[i] += count[i - 1];
}
int[] result = new int[arr.length];
for (int j = arr.length - 1; j >= 0; j--) {
result[count[arr[j] - min] - 1] = arr[j];
count[arr[j] - min]--;
}
return result;
}
// 辅助函数:将int[] 打印出来
private static void arrPrint(int[] arr) {
StringBuilder str = new StringBuilder();
str.append("[");
for (int v : arr) {
str.append(v + ", ");
}
str.delete(str.length() - 2, str.length());
str.append("]");
System.out.println(str.toString());
}
}
实例的动画演示如下: