计数排序的思想
计数排序,当数据范围max不大的时候,可以使用一个长度为max+1,即[0,max]的数组用以存储数据出现的次数。根据这个数据出现的次数,通过一定的计算,就可以知道这个数据之前到底有多少个数据,那么自然就很容易知道这个数据在总体数据中的位置了。利用这个思想,我们就可以对一个范围不大的数组进行计数排序。
计数排序的时间复杂度
计数排序的时间复杂度是O(n),当然这个前提是数据范围不大的时候,即数据的最大值max不会比数组的总数据个数n大很多。
计数排序的稳定性
当正向遍历的时候,计数排序不是稳定的排序算法,数据的位置会发生改变。
当反向遍历的时候,计数排序就是稳定的排序算法,数据的位置不会改变。
计数排序是否为原地排序算法
计数排序很明显用到了额外的存储空间来存储排好序的数据,而且还使用了计数数组来存储每个数据出现的次数。所以肯定不是原地排序算法。
图解
代码
package com.study.algorithm.sort;
/**
* 计数排序
*
*/
public class CountingSort {
// 计数排序,a是数组,n是数组大小。假设数组中存储的都是非负整数。
public static void countingSort(int[] a, int n) {
if (n <= 1) {
return;
}
// 查找数组中数据的范围
int max = a[0];
for (int i = 1; i < n; ++i) {
if (max < a[i]) {
max = a[i];
}
}
// 申请一个计数数组c,下标大小[0,max]
int[] c = new int[max + 1];
// 计算每个元素的个数,放入c中
for (int i = 0; i < n; ++i) {
c[a[i]]++;
}
// 依次累加
for (int i = 1; i < max + 1; ++i) {
c[i] = c[i-1] + c[i];
}
// 临时数组r,存储排序之后的结果
int[] r = new int[n];
// 计算排序的关键步骤了,有点难理解
for (int i = n - 1; i >= 0; --i) {
int index = c[a[i]]-1;
r[index] = a[i];
c[a[i]]--;
}
// 将结果拷贝会a数组
for (int i = 0; i < n; ++i) {
a[i] = r[i];
}
}
public static void printArr(int[] arr,int n){
for (int i = 0; i < n ; i++) {
System.out.print("["+arr[i]+"]");
}
}
public static void main(String[] args) {
int[] arr={2,5,3,0,2,3,0,3};
countingSort(arr,8);
printArr(arr,8);
}
}
计数排序的应用场景
计数排序适合在数据范围不大的时候使用,如果数据范围最大值max比数据总个数n大很多就不适合了;而且计数排序只能对大于等于0的整数使用,所以有时候需要做好转化。