内容介绍
计数排序简介
我们知道目前排序速度最快的是时间复杂度为O(nlogn)的排序算法,如快速排序、归并排序和堆排序。其中O(logn)是利用了分治思想进行数据二分远距离比较和交换元素的位置。之前的算法都是基于元素比较的,有没有一种算法,它的时间复杂度小于O(nlogn)呢?这样的算法是存在的。计数排序就是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法。
计数排序适用于数据量很大,但是数据的范围比较小的情况。比如对一个公司20万人的年龄进行排序,要排序的数据量很大,但是年龄分布在0 ~ 134岁之间(最大年龄数据来源吉尼斯世界记录)。
计数排序的思想
使用一个辅助数组,遍历待排序的数据,待排序数据的值就是辅助数组的索引,辅助数组索引对应的位置保存这个待排序数据出现的次数。最后从辅助数组中取出待排序的数据,放到排序后的数组中。
计数排序动画演示
一般没有特殊要求排序算法都是升序排序,小的在前,大的在后。数组由{6, 8, 9, 5, 3, 2, 1, 7, 8, 5} 这10个无序元素组成。
计数排序分析
通过上面的动画演示,我们可以将计数排序分为两个过程:
- 统计过程
- 排序过程。
假设我们要排序的数据是10个0到9的随机数字,例如{6, 8, 9, 5, 3, 2, 1, 7, 8, 5} 这10个数据,如下图所示:
- 统计过程
取出元素6,放到辅助数组索引6的地方,辅助数组记录数据6出现1次,效果如下图:
取出元素8,放到辅助数组索引8的地方,辅助数组记录数据8出现1次,效果如下图:
取出元素9,放到辅助数组索引9的地方,辅助数组记录数据9出现1次,效果如下图:
依次类推。中间省略一部分。
再次取出元素8,放到辅助数组索引8的地方,辅助数组记录数据8出现2次,效果如下图:
最终辅助数组效果如下:
这个辅助数组统计了每个数据出现的次数,最后遍历这个辅助数组,辅助数组中的索引就是元素的值,辅助数组中索引对应的值就是这个数据出现的次数,放到排序后的数组中。
- 排序过程
辅助数组索引1的对应的数据1放到排序后数组中,效果如下:
辅助数组索引2的对应的数据2放到排序后数组中,效果如下:
依次类推,取出统计数组中的数据放到排序后的数组中,中间省略部分。
辅助数组索引5的对应的数据5放到排序后数组中,效果如下:
再次将辅助数组索引5的对应的数据5放到排序后数组中,效果如下:
依次类推,中间省略部分。
最终排序后的效果如下:
计数排序代码编写
public class CountSortTest {
public static void main(String[] args) {
int[] arr = new int[] {
6, 8, 9, 5, 3, 2, 1, 7, 8, 5};
countSort(arr);
System.out.println("排序后:" + Arrays.toString(arr));
}
// 假设我们要排序的数据是10个0到9的随机数字
public static void countSort(int[] arr) {
// 1.得到待排序数据的最大值
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
int num = arr[i];
if (num