计数排序与计数排序的思想都来自于桶排序,桶排序的大体思想为:若N个关键字均可以映射到0-M的整数范围内,则可以建立M+1大小的桶,然后依次遍历N个关键字,依次将他们放到自己对应的那个桶里面,最后从0开始依次将桶中的元素倒出即可。
计数排序:
大体思想:假设序列值均为整数,首先获得序列的最大值valueMax和最小值valueMin,建立一个大小为(valueMax-valueMin+1)的数组count,遍历该序列data,count[data[i]-valueMin]++。如此,count中第k个位置上的元素即为k+valueMin出现的次数,再根据次数复制到data中去,完成排序。
代码如下:
public static void countSort(int[] data) {
// 找到最小值 最大值
int valueMax=data[0],valueMin=data[0];
for(int i=0;i<data.length;i++) {
if(data[i]>valueMax) {
valueMax=data[i];
}
if(data[i]<valueMin) {
valueMin=data[i];
}
}
int[] count=new int[valueMax-valueMin+1];
for(int temp:data) {
count[temp-valueMin]++;
}
//把值复制到data中
for(int i=0,k=0;i<count.length;i++) {
for(int j=0;j<count[i];j++) {
data[k++]=i+valueMin;
}
}
}
复杂度分析:
显然计数排序是N的线性范围复杂度的排序,其时间复杂度为O(N)。空间复杂度由于建了M个桶,因此其空间复杂度为O(M)。
此外计数排序是稳定的。
基数排序:
大体思想:一般用于多个关键字的排序(例如扑克牌中的花色和面值大小),基数排序分为两种主位优先和次位优先两种,下面主要介绍用的跟广泛的次位优先。所谓次位优先,先以次位关键字建立桶,将所有元素按照关键字依次扔进桶中,再依次从小到大从桶中放回到原数组。对于多个关键字依次从次到主重复上述操作即可完成排序。
本文以三位数的整数排序为例,将三位数的百位、十位、个位作为关键字,显然百位是主位。其代码如下:
public static void radixSort(int[] data) {
//以三位数的整数为例
int N=10;
//创建桶
List<LinkedList<Integer>> buck=new ArrayList<LinkedList<Integer>>(N);
for(int i=0;i<N;i++) {
buck.add(new LinkedList<Integer>());
}
for(int i=0,k=1;i<3;i++,k*=10) {// 从低位数第i位 0=个位 1=百位
int temp=0;
for(int j=0;j<data.length;j++) {
temp=data[j]%(10*k)/k;//取出i位上的数
buck.get(temp).add(data[j]);
}
//往data中倒
for(int j=0,m=0;j<N;j++) {
while(buck.get(j).size()>0) {
data[m++]=buck.get(j).remove();
}
}
}
}
复杂度分析:
从上述代码中可以发现,花费时间为3*(N+10),其时间复杂度为O(D(N+M)),其中D为关键字的个数,M为桶的个数,N为序列长度。
空间复杂度为O(N),本文中所用的list均需要存放所有元素。