计数排序是一种适合于对数组中最大值与最小值相差不大的数组进行排序。
假设对数组 [1, 0, 5, 6, 4, 7, 4, 2, 8, 9, 0, 1, 4, 2, 0, 3, 5, 7, 8, 6] 进行计数排序。我们可以创建一个临时数组a[max+1],临时数组的大小为10,临时数组的下标正好与原数组中的数据对应。我们可以遍历原数组,每遍历一个数,对应临时数组的下标元素就加一。
比如无序数组的第一个数是1,那么临时数组下标为1的元素就加一:
第二个数是0,那么临时数组下标为0的元素加一:
直到未排序数组中的所有数据都遍历完成,此时临时数组的状态为:
此时,直接遍历临时数组,输出临时数组的下标,元素的值是几,就输出几次,排序就完成了。
代码实现
public void countSortE(int[] arr) {
if (arr == null || arr.length < 2) return;
// 找到最大值
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
}
// 定义临时数组
int[] temp = new int[max + 1];
// 统计每个元素出现的次数
for (int i = 0; i < arr.length; i++) {
temp[arr[i]]++;
}
int k = 0;
// 将临时数组统计好的数据汇总到原数组
for (int i = 0; i < temp.length; i++) {
for (int j = temp[i]; j > 0; j--) {
arr[k++] = i;
}
}
}
优化
前面的代码中,我们是根据max的大小来创建临时数组,假设原数组的最大值为10005,最小值为10000,那这样创建就不合理,所以可以根据最大值与最小值的差值来创建临时数组。
优化后的代码
public void countSort(int[] arr) {
if (arr == null || arr.length < 2) return;
// 找到数组的最大最小值
int max = arr[0];
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
if (arr[i] < min) min = arr[i];
}
// 定义临时数组
int[] temp = new int[max - min + 1];
// 统计每个元素出现的次数
for (int i = 0; i < arr.length; i++) {
temp[arr[i] - min]++;
}
int k = 0;
// 将临时数组统计好的数据汇总到原数组
for (int i = 0; i < temp.length; i++) {
for (int j = temp[i]; j > 0; j--) {
arr[k++] = i + min;
}
}
}
性质
1.时间复杂度:O(n+k) 2.空间复杂度:O(k) 3.稳定排序 4.非原地排序
注:k为临时数组的大小