一. 计数排序的定义
计数排序是一种非比较型整数排序算法。它的核心思想是对于待排序的数组,统计每个数出现的次数,然后根据次数从前往后依次输出。
计数排序的时间复杂度为O(n),这使得它比其他排序算法快得多,特别是当待排序的数据量很大,但是其空间复杂度却较高。
优点:
- 时间复杂度低,只需要O(n)的时间复杂度。
- 易于实现。
- 对于数据范围很小的情况下,它可以达到稳定性。
缺点:
- 空间复杂度高,需要额外的存储空间。
- 仅适用于整数数据的排序。
- 不适用于浮点数和负数的排序。
二. 计数排序的流程
计数排序的流程分析:
- 首先确定数列的范围,并创建一个对应的桶列表,每个桶代表一个数列中的值;
- 然后遍历原数列,并统计每个数列中值出现的次数,并将次数加入对应的桶中;
- 接下来将桶列表转换为前缀和数组,即统计每个桶前面所有桶的数值总和;
- 最后遍历原数列,对于每个数,将它插入对应的桶中的对应位置,位置确定的方法是:将该数的数值减去数列的最小值,然后加一,再在前缀和数组中找到该数的前一个数的位置,这个位置即为该数的插入位置。
通过这样的步骤,我们就可以得到一个从小到大排序的数列。
三. 计数排序的图解
整体流程:
四. 计数排序的代码
下面是TypeScript实现的计数排序代码,带有详细的注释:
function countingSort(array: number[]): number[] {
const n = array.length;
// 计算数列最大值和最小值
let max = array[0];
let min = array[0];
for (let i = 1; i < n; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
// 统计数列中每个值出现的次数
const count = new Array(max - min + 1).fill(0);
for (let i = 0; i < n; i++) {
count[array[i] - min]++;
}
// 累加数组
for (let i = 1; i < count.length; i++) {
count[i] += count[i - 1];
}
// 从后向前遍历原始数组,按照统计数组中的位置放置元素
const res = new Array(n);
for (let i = n - 1; i >= 0; i--) {
res[--count[array[i] - min]] = array[i];
}
return res;
}
在上面的代码中,我们首先定义了一个名为countingSort的函数
-
该函数接受两个参数:待排序数组arr和数组中元素的最大值maxValue。
-
接下来,我们创建了一个大小为maxValue + 1的数组count,该数组用于统计数字出现的次数。
-
接着,我们使用for循环对数组arr中的每个元素进行处理,并统计每个数字出现的次数。
-
最后,我们遍历数组count,并使用for循环把它的元素按照统计的次数逐个输出到结果数组中,最终得到有序数组。
五. 计数排序的时间复杂度
计数排序的时间复杂度分析如下:
-
预处理:建立一个计数数组,把输入数组中每个数字出现的次数存入计数数组。
- 时间复杂度:O(k),其中k是数字的取值范围。
-
累加:遍历计数数组,把每个数字出现的次数加上它前面数字出现的次数。
- 时间复杂度:O(k)
-
输出:遍历输入数组,把每个数字存入输出数组对应的位置。
- 时间复杂度:O(n),其中n是输入数组的长度。
因此,计数排序的总时间复杂度为:O(k + n)。
计数排序对于长度较短,数字取值范围较小的数组是非常高效的。
六. 计数排序的总结
计数排序是一种特殊的排序算法:
- 其不同于其他算法的地方在于它不依赖于比较,而是通过计算每个元素的出现次数,来确定每个元素的排名。
计数排序的复杂度为 O(n+k),其中 k 是数组中出现的数的范围。
- 因此,计数排序非常适合数据范围较小的数组,并且可以很快地对数组进行排序。
- 但是,由于需要额外的存储空间来存储每个数的出现次数,因此不适合大数据量的数组。
总的来说,计数排序是一种非常有效的排序算法,在适当的情况下可以使用。
coderwhy公众号分享各种编程技术、生活、感悟,欢迎关注、交流、分享。