计数排序是一种非基于比较的排序算法,其空间复杂度和时间复杂度均为O(n+k),其中k是整数的范围。基于比较的排序算法时间复杂度最小是O(nlogn)的。注意:计数排序对于实数的排序是不可行的(下面会解释)。该算法于1954年由 Harold H. Seward 提出。
下面根据一个示例来讲解,比如现在有个待排序的整数序列A={-1, 2, 0, 4, 3, 6, 5, 8, -2, 1, 3, 0, 3,6, 5, 2}。首先我们花O(n)的时间扫描一下整个序列,可以得到max=8,min=-2。然后我们建立一个新的数组C,长度为(max-min+1)=11。
数组C如下所示:
数组index为0的元素记录的值是-2出现的次数,依次index为1的元素记录的是-1出现的次数。
此时我们再扫描一下数组A,比如对于-1,我们的操作是:-1-min=-1-(-2)=1;C[1]++。对于2,我们的操作是:2-(-2)=4;C[4]++。这样我们又花了O(n)的时间。操作结果是:
最后我们便可以输出目标整数序列了,具体的逻辑是遍历数组C,比如C[0]=1,则输出一次min+index即(-2+0)=-2;C[1]=1,则输出一次-1;C[2]=2,则输出两次0,以此类推。目标序列为:
-2, -1, 0, 0, 1, 2, 2, 3, 3, 3, 4, 5, 5, 6, 6, 8
分析可得,我们扫描了两次数组A,一次数组C,所以计数排序的时间复杂度为2(n)+n+k(扫描数组C的时间复杂度是n+k)。空间复杂度是:n+k(n是数组A的空间,最后输出目标整数序列时可以直接存储在A中;k是数组C的空间)。故其空间复杂度和时间复杂度均为O(n+k)。
前面说了计数排序对于实数的排序是不可行的,这是因为我们无法根据最小值和最大值来确定数组C的长度,比如0.1和0.2之间有无限个实数。但是如果限制实数精度,依然是可行的,比如说数组A中的数字只保留5位小数。但是这已经不是实数了,相当于整数,因为相当于给原来数组A的所有数都乘以10^5。
具体的Java代码如下所示(countSort1在空间上作了一点优化,countSort2是最原版的实现,countSort1是上述描述的实现):
public class CountSortTest {
public static void main(String[] args) {
int [] A = {-1, 2, 0, 4, 3, 6, 5, 8, -2, 1, 3, 0, 3, 6, 5, 2};
int [] B = countSort2(A);
for(int index = 0; index < B.length; index++)
System.out.print(B[index] + " ");
System.out.println();
countSort1(A);
for(int index = 0; index < A.length; index++)
System.out.print(A[index] + " ");
}
private static void countSort1(int [] A) {
int min = 0, max = 0;
int [] C = null;
for(int index = 0; index < A.length; index++) {
if(A[index] < min) {
min = A[index];
continue;
}
if(A[index] > max) max = A[index];
}
C = new int[max - min + 1];
for(int index = 0; index < A.length; index++) {
C[A[index] - min]++;
}
int a = 0;
for(int index = 0; index < C.length; index++) {
for(int count = 0; count < C[index]; count++){
A[a++] = index + min;
}
}
}
private static int [] countSort2(int [] A) {
int min = 0, max = 0;
int [] C = null;
int [] B = new int[A.length];
for(int index = 0; index < A.length; index++) {
if(A[index] < min) {
min = A[index];
continue;
}
if(A[index] > max) max = A[index];
}
C = new int[max - min + 1];
for(int index = 0; index < A.length; index++) {
C[A[index] - min]++;
}
for(int index = 1; index < C.length; index++){
C[index] = C[index] + C[index - 1];
}
for(int index = A.length - 1; index > -1; index--) {
B[C[A[index] - min] - 1] = A[index];
C[A[index] - min]--;
}
return B;
}
}
程序运行结果: