计数排序基本思想:
计数排序是一个非基于比较的排序算法。它的优势在于在对一定范围内的整数排序时,快于任何比较排序算法。
当然这是一种牺牲空间换取时间的做法。
-
输入的线性表的元素属于有限偏序集S;
(意思是说,输入的数据必须是一定范围内的数据,比如高考成绩,必须要是0-750) -
计数排序的基本思想是先统计数组中每个整数在数组中出现的次数,然后按照从小到大的循序将每个整数按照它出现的次数填到数组中。比如输入的数组是[2,3,4,2,3,2,1],通过循环可以直到1出现了1次,2出现了3次,3出现了2次,4出现了1次。然后按照先后循序依次在数组中填入1个1,3个2,2个3,1个4,就可以得到排序后的数组[1,2,2,2,3,3,4]。
下图是借鉴的其他博主的GIF
代码实现:
package count;
public class Count {
public static int[] CountSort(int arr[]) {
//新建一个结果集数组,用来存放排序后的数据
int[] result = new int[arr.length];
//找到待排序数组的最大值、最小值,因为这样就没必要开辟多余的空间,从而节省空间
int max = arr[0], min = arr[0];
for (int i : arr) {
if (i > max) {
max = i;
}
if (i < min) {
min = i;
}
}
int k = max - min + 1;
//开辟的空间是待排序数组中max-min+1,
//这样就可以尽可能避免开辟多余的空间
int[] count = new int[k];
//这是统计数组,统计待排序数组中每个数据出现的次数。
//放到count数组中,减去min是为了让数据从cout[0]位置开始存储。
for (int i = 0; i < arr.length; i++) {
count[arr[i] - min]++;
}
//累加数组,对count数组进行操作,
//比如数组:[1,0,2,3,2,3,1]
//统计数组:[1,2,2,2]
//累加数组:[1,3,5,7]
for (int i = 1; i < count.length; i++) {
count[i] = count[i] + count[i - 1];
}
//逆序遍历,待排序数组
//按照上边举例数组,i=6,arr[6]为1,arr[6]-0为1,count[1]为3,减去1为2,
//所以arr[6]为1这个值,最终放的位置是result[2]
//这个1在待排序数组所有的1中,位置最后
//在result数组所有1中位置也是最后的,所以这保证了排序的稳定性
//那如果再遇到下一个1是什么情况呢?
//当i=0,arr[0]为1,arr[0]-0为1,count[1]为2,(因为刚才执行了依次--count[1]),减去1为1,
//所以arr[0]为1这个值,最终放的位置是result[1],能够保证排序先后它们的相对位置没有发生变化,所以排序是稳定的
for (int i = arr.length - 1; i >= 0; i--) {
result[--count[arr[i]-min]] = arr[i];
}
return result;
}
}
弄一个测试类:
package count;
import java.util.Random;
public class TestCount {
public static void main(String[] args) {
Random random = new Random();
int[] arr = new int[1000];
for (int i = 0; i < 1000; i++) {
arr[i] = random.nextInt(10);
}
int[] arr1 = Count.CountSort(arr);
for (int i = 0; i < arr1.length; i++) {
System.out.println(arr1[i]);
}
}
}
复杂度:
数组的长度为n,整数的范围为k
时间复杂度为:O(n+k)
创建了一个长度为k的辅助数组count
空间复杂度为:O(k)
当k比较小时,无论从时间复杂度和空间复杂度来说,计数排序都是非常高效的算法,
当k比较大时,计数排序就没有其他算法高效。