# 排序算法-8---计数排序
概念
计数排序(Counting sort)是一种稳定的线性时间排序算法。该算法于1954年由 Harold H. Seward 提出。计数排序使用一个额外的数组 C ,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C 来将 A中的元素排到正确的位置。
解题思路
计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。
例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。当然,如果有多个元素具有相同的值时,我们不能将这些元素放在输出序列的同一个位置上。解决方案就是要反向填充目标数组,以及将每个数字的统计减去1。
运行过程
计数排序的运行过程如下:
- 1、找出待排序的数组中最大和最小的元素;
- 2、统计数组中每个值为i的元素出现的次数,存入数组 C 的第 i 项 ;
- 3、对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加) ;
-
4、反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
image
例如:
一组学生的成绩为{2,5,3,0,2,3,0,3},这时候我们可以用一个数组A[8]存储这组数据,然后按照桶排序的标准,将数据分成0-5,建立一个初始化的数组C[6]表示桶,然后遍历一遍A[8],得到一个新的C[6]
image
从图中可以看出分数为3的有3人,小于3的分数有4人,所以可以得出一个R[8]
image
如何快速确认成绩在数组中的位置呢,当然不能通过我们肉眼看,我们的思路是进行对C[6]进行一次顺序求和,所以C[6]的存储情况变成了如下:
image
重点来了!
我们对数组A[8]进行倒序遍历,当第一个碰到3的时候,因为在C[3] = 7,所以,在整个数组A中,小于等于3的个数为7个,所以我们应该将3放在R[8]的第6位,然后C[3] = C[3] - 1,以此类推~
image代码
java
//针对c数组的大小,优化过的计数排序
public class CountSort{
public static void main(String []args){
//排序的数组
int a[] = {3, 6, 4, 1, 3, 4, 1, 4};
int b[] = countSort(a);
for(int i : b){
System.out.print(i + " ");
}
}
public static int[] countSort(int []a){
int b[] = new int[a.length];
int max = a[0], min = a[0];
for(int i : a){
if(i > max){
max = i;
}
if(i < min){
min = i;
}
}
//这里k的大小是要排序的数组中,元素大小的极值差+1
int k = max - min + 1;
int c[] = new int[k];
for(int i = 0; i < a.length; ++i){
c[a[i]-min] += 1;//优化过的地方,减小了数组c的大小
}
for(int i = 1; i < c.length; ++i){
c[i] = c[i] + c[i-1];
}
for(int i = a.length-1; i >= 0; --i){
b[--c[a[i]-min]] = a[i];//按存取的方式取出c的元素
}
return b;
}
}
性能
计数排序时间复杂度为O(n+k);空间复杂度为O(n+k);是稳定的排序算法。
需要注意的是:计数排序算法之所以能取得线性计算时间的上界是因为对元素的取值范围作了一定限制,即k=O(n)。如果k=n2,n3,..,就得不到线性时间的上界
参考资料:
维基百科
网友评论