对数组 [4,1,5,0,8,3,7,5,1] 进行排序,我们可以开辟一大小为10的辅组数组空间help[10],初值均为0。扫描数组,扫描到4,help[4]+1;扫描到1,help[1]+1;扫描到5,……。最后整个help数组是[1,2,0,1,1,2,0,1,1,0]。扫描help数组,元素非0代表着与下标值相同的这个数存在,个数等于元素值,能得到排序结果: [0,1,1,3,4,5,5,7,8]。
计数排序就是这种不基于比较的排序,只不过对上面举例的方法进行了更进一步改进。
限制:输入的元素必须要属于有限集合。
时间复杂度为O(n+k),空间复杂度为O(n+k)。n为待排序数组的规模,k为辅助数组的规模。
例如在上面的例子中[4,1,5,0,8,3,7,5,1]的最大值为8,最小值是0,可以构建大小为9的辅助数组help,统计值次数后,help数组为[1,2,0,1,1,2,0,1,1],累加计数后help数组为[1,3,3,4,5,7,7,8,9]。下面详细描述反向遍历原始数组过程:
结果数组为[0,1,1,3,4,5,5,7,8]。
public class CountSort{ public static int[] countSort(int[]arr){ int[] res = new int[arr.length]; //结果数组 //求最大值和最小值 int max = arr[0],min = arr[0]; for(int value : arr){ if(value > max){ max = value; } if(value < min){ min = value; } } int k= max - min + 1; int[] help=new int[k]; for (int value : arr) { help[value - min] += 1; } //累加计数 for(int i=1; i < help.length; ++i){ help[i]=help[i] + help[i-1]; } //反向遍历 for(int i= arr.length-1; i >= 0; --i){ res[--help[arr[i] - min]]= arr[i]; } return res; } }
计数排序是一种空间换时间的算法,优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法,但当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序。要求排序元素的取值要在一定范围内,并且比较集中,其有限偏序集不能太大,才能发挥出计数排序的优势。