【算法精解】计数排序
- 基本认识:是一个特殊的桶排序。基于比较排序的排序方法,其复杂度无法突破nlogn 的下限,但是非比较排序却可以突破该下限,甚至达到O(n)的时间复杂度。当有n个数据时,所处的范围并不大,最大值是k,那么就划分k个桶。每个桶都存储相同的数据。这样可以省掉桶内排序的时间。
- 时间复杂度:因为整个过程只涉及到扫描遍历操作,所以时间复杂度是O(n+k)。【n为元素个数,k为待排序最大值与最小值之差】
- 优缺点:适用于待排序数据的范围不大的情况,若范围比较大,会产生很多空桶,造成空间浪费。
- 适用场景:以高考为例,假设某省有50万考生,高考成绩满分是750分,我们可以分配751个桶(最低0分,最高750分),遍历50万考生的成绩,划分到751个桶中,最后只需要依次扫描桶,就可以输出排序好的数据。
- 排序思路
对A[8]进行排序,其中A[8] = {0,2,3,5,2,3,0,3},min=0,max=5
1、遍历A[8],得到C[6]={2,0,2,3,0,1}。//C[0]=2的含义为:值为0的数据再A[]中有两个
2、对C[6]数组顺序求和,C[k]存储小于等于分数k的考生个数,C[6]={2,2,4,7,7,8}。//C[1]=2的含义为:A[]中 <= 1的值有两个
3、新建一个数据R[8]用于存储排序好的数据。
4、从后向前遍历数据A[8],A[8]的倒数第一个数为3,C[6]下标为3的数为7,表明数组A中小于等于3的有7个人,所以讲这个3放入R[8]的第7个位置,C[6]下标为3位置的数变为7-1=6。此时R[8]={0,0,0,0,0,0,3,0},C[6]={2,2,4,6,7,8}。
5、依次类推,扫描A[8]剩下的数据。最后R[8]就是排序好的数据。
- Java代码实现
public class Order {
public static void main(String[] args) {
int[] beforArr = {13, 19, 15, 11, 7, 11, 25, 21, 29, 17, 15, 14, 17, 29, 28, 22};
System.out.println("待排序数据:" + Arrays.toString(beforArr));
int[] afterArr = countingSort(beforArr);
System.out.println("排序之后数据:" + Arrays.toString(afterArr));
}
public static int[] countingSort(int[] beforArr){
int max = beforArr[0];
int min = beforArr[0];
for (int arr : beforArr) {
if (arr > max){
max = arr;
}
if (arr < min){
min = arr;
}
}
int size = max - min + 1;
int[] bucket = new int[size];
for (int i=0; i<beforArr.length; i++){
bucket[beforArr[i]-min] ++;
}
for (int i=1; i < size; i++){
bucket[i] = bucket[i] + bucket[i-1];
}
int[] afterArr = new int[beforArr.length];
for (int i=beforArr.length-1; i>=0; i--){
int index = bucket[beforArr[i]-min] - 1;
afterArr[index] = beforArr[i];
bucket[beforArr[i]-min] --;
}
return afterArr;
}
}