什么是计数排序呢?
简单来说,就是通过额外开辟一个数组或者类似的集合空间,将原数组待排序元素进行整理放到这个新开辟的数组中,最后,再将这个数组排序好的元素重新填充到原数组中,可以看出来,这个计数排序是需要额外消耗空间的。
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数(局限性)。
计数排序算法Java实现
计数排序算法步骤大概有三个步骤:
- 建一个长度为K+1的的数组C,里面的每一个元素初始都置为0(Java里面默认就是0)。
- 遍历待排序的数组,计算其中的每一个元素出现的次数,比如一个key为i的元素出现了3次,那么C[i]=3。
- 累加C数组,获得元素的排位,从0开始遍历C, C[i+1]=C[i]+C[i-1]
- 建一个临时数组T,长度与待排序数组一样。从数组末尾遍历待排序数组,把元素都安排到T里面,直接从C里面就可以得到元素的具体位置, 不过记得每处理过一个元素之后都要把C里面对应位置的计数减1。
下面用代码来实现一下这个过程,大家可以参照代码注释一块儿理解,
public class CountSort {
public static void main(String[] args) {
int[] arr = {11,48,15,7};
System.out.println(Arrays.toString(countSort(arr)));
}
public static int[] countSort(int[] arr){
//最大最小值初始化
int min=arr[0],max=arr[0];
//寻找最大最小值
for(int i=0;i<arr.length;i++){
if(arr[i] > max){
max = arr[i];
}
if(arr[i] < min){
min = arr[i];
}
}
//定义一个额外的数组,相当于是一个空桶,可以存放的下标长度是从 min 到max的长度大小
int[] bucket = new int[max-min+1];
//初始化桶填充,这里全部为0
Arrays.fill(bucket, 0);
//{11,48,15,7} ,保证原数组的元素在bucket中都能占据一个位置,
for(int i=0;i<arr.length;i++){
bucket[arr[i]-min]++;
}
//数组内容回填
int index=0,i=0;
while(index < arr.length){
if(bucket[i] != 0){
arr[index] = i + min;
bucket[i]--;
index++;
}else{
i++;
}
}
return arr;
}
}
运行一下这段程序,可以看到已经排好序,
算法分析
当输入的元素是n 个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量内存。N表示原始数组元素个数,k表示桶的个数,也就是数组c的长度
综合来看:
1、最佳情况:T(n) = O(n+k)
2、最差情况:T(n) = O(n+k))
3、平均情况:T(n) = O(n+k)