常见的排序算法,例如:冒泡,选择,快速等等。他们都是基于比较实现的排序算法。最快的快速排序算法的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。而计数排序是一种特殊的排序算法,它的时间复杂度为 O ( 3 n + k ) O(3n+k) O(3n+k),在数据较多,数据范围较小的情况下,计数排序比快速排序的效率更高。与此同时,计数排序是稳定的,相同的数据相对顺序保持不变。
示例:待排序数组array为[98, 97, 96, 98, 95, 100, 96, 98, 100, 99]。
算法步骤:
1、找到最小值min,最大值max,得到数据的极差1range = max - min + 1。
2、创建计数数组count,大小为range。
3、顺序遍历原数组,索引为i,数据element = array[i],映射到count数组中索引为index = element - min,将count[index]加1。表示数组中的元素element的个数加1。
4、顺序遍历count数组,索引为i,计算count[i] = count[i - 1] + count[i]。
5、逆序遍历原数组array,索引为i,数据element = array[i],映射到count数组中索引为index = element - min,将count[index]减1后的数据为j,然后将element保存到结果数组(res)中的索引为j的位置。
演示视频如下:
计数排序
由此可以看出:算法共有四次循环,其中三次遍历原数组,时间复杂度为
O
(
n
)
O(n)
O(n),若数据范围为k,则遍历count数组的时间复杂度为
O
(
k
)
O(k)
O(k),故总的时间复杂度为
O
(
3
n
+
k
)
O(3n+k)
O(3n+k)。不计算输出数组的情况下,空间复杂度为
O
(
k
)
O(k)
O(k)。
可以发现,数据范围对计数排序的复杂度影响较大。因此当数据范围非常大的时候不宜使用此算法。
Java代码实现如下:
/**
* 计数排序
* <p>创建日期:2020-06-05 11:56</p>
*
* @author PengHao
*/
public class CountSort {
/**
* <p>计数排序</p>
* <p>时间复杂度为O(3n+k)</p>
* <p>空间复杂度为O(k)</p>
*
* @param src 待排序数组
* @return 排好序的数组
*/
public static int[] countSort(int[] src) {
// 数组中的最小值
int min = src[0];
// 数组中的最大值
int max = src[0];
for (int value : src) {
if (value < min) {
min = value;
} else if (value > max) {
max = value;
}
}
// 数组中的数据范围
int range = max - min + 1;
// count[i]表示数组src中数据min + i的个数
int[] count = new int[range];
for (int value : src) {
count[value - min]++;
}
// 累计,使得count[i]保存src中小于等于min + i的数据的个数
for (int i = 1; i < range; i++) {
count[i] += count[i - 1];
}
// 结果数组
int[] res = new int[src.length];
// 倒序遍历原数组,保持排序的稳定性
for (int i = src.length - 1; i >= 0; i--) {
// 获取数据在count数组中的索引
int index = src[i] - min;
// 个数减1
count[index]--;
// 数据src[i]排序后的索引是count[index]
res[count[index]] = src[i];
}
return res;
}
}
代码没有进行安全检查,所以待排序数组不能为空,并且至少有1个元素。
数学中极差的定义为:range = max - min。 ↩︎