网站首页 > 技术文章 正文
摘要
计数排序本质就是统计不同元素出现的次数,然后将元素依次从小到大放置,每个元素看统计的次数,就紧挨着放置几个同样的元素。
看似简单的处理,在算法中,会依据统计的元素次数推算出每个元素的索引位置,这就是算法的魅力。
逻辑
统计每个整数在序列中出现的次数,进而推导出每个整数在有序序列中的索引。
这是通过空间换取时间的方式。
流程
- 获取序列中的最大值
- 统计每个元素出现的次数
- 按照顺序进行赋值
实现
根据获取到的最大值,创建序列统计序列中的元素出现的次数。创建的序列大小是 max+1,让最大值也可以放在统计序列中。
// 找出最大值
int max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
// 开辟内存空间,存储每个整数出现的次数
int[] counts = new int[1 + max];
counts 数组的索引就是序列中的元素,存放的是序列中元素出现的次数。
// 统计每个整数出现的次数
for (int i = 0; i < array.length; i++) {
counts[array[i]]++;
}
现在就通过遍历 counts 数组排序序列中的元素,这里设置一个 index 变量,用于多次出现的元素,每放置一个元素,index 就减 1,直到 index 为 0 时,再遍历下一个索引位置。
// 根据整数出现次数,对整数进行排序
int index = 0;
for (int i = 0; i < counts.length; i++) {
while (counts[i]-- > 0) {
array[index++] = i;
}
}
缺点
- 无法对负整数进行排序,这里只能排序 0 到 max 元素的序列
- 极其浪费内存空间
- 是不稳定的排序
进阶
看上面实现计数排序的缺点,这里就去更改它的缺点。
可以对负整数排序,获取序列中的 min 和 max,创建计数数组的长度为 max-min。 在计数数组中,当前位置 count 加上上一个位置的 count。那么当前元素在序列中的索引就是计数数组中的 count - 1。
比较难理解的是计数数组的 count 和 array 元素的索引的对应关系。
这块可以理解,计数数组中存放着该元素的起始位置和有几个相同的元素。即类似于 range(index, count) 的情况
首先是找到序列中的 min 和 max。
// 找出最大值
int max = array[0];
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
接下来就是计数排序的重点了。这里做了两次处理,第一次统计序列中元素出现的次数。第二次将统计数组中每个索引位置的数字加上前一个索引位置的数字。这就可以计算出每个元素在序列中的位置就是 array[i]-min
// 开辟内存空间,存储次数
int[] counts = new int[max-min+1];
// 统计每个整数出现的次数
for (int i = 0; i < array.length; i++) {
counts[array[i]-min]++;
}
// 累加次数
for (int i = 1; i < counts.length; i++) {
counts[i] += counts[i-1];
}
最后将序列从后往前遍历,通过计算出的索引,依次排序。从最后开始遍历就是增加序列的稳定性。
// 从后往前遍历数组,放在有序数组中的位置
int[] newArray = new int[array.length];
for (int i = array.length - 1; i >= 0; i--) {
newArray[--counts[array[i]-min]] = array[i];
}
// 将有序数组覆盖到 array
for (int i = 0; i < newArray.length; i++) {
array[i] = newArray[i];
}
技巧点/注意点
- 计数数组中的索引是序列元素-min
- 从后往前遍历数组,是保证数组的稳定性,如果从头开始,相同元素在原来数组中的相对位置会发生变化
- 计数数组中的 count 在赋值一次后要进行 -- 操作。
计数排序的精妙点就在通过两次的统计,就可以计算出每个元素在序列中的位置。
时间和空间复杂度
- 最好、最坏、平均时间复杂度:O(n+k)
- 空间复杂度:O(n+k)
- 属于稳定排序
k 是整数的取值范围
猜你喜欢
- 2024-11-10 时间复杂度 时间复杂度取决于
- 2024-11-10 选择排序代码及时间空间复杂度 简单选择排序时间复杂度分析
- 2024-11-10 排序算法汇总 排序算法 简书
- 2024-11-10 「时间管理」JavaScript算法时间、空间复杂度分析
- 2024-11-10 数据结构与算法——常见排序算法分享
- 2024-11-10 快速排序算法 快速排序算法的平均时间复杂度为
- 2024-11-10 上个厕所的功夫,就学会了“快速排序”算法
- 2024-11-10 常用排序方法使用场景和性能对比分析
- 2024-11-10 数据结构:复杂度分析(时间复杂度和空间复杂度)
- 2024-11-10 堆排序代码及时间空间复杂度 堆排序的算法及代码实现
你 发表评论:
欢迎- 11-13第一次养猫的人养什么品种比较合适?
- 11-13大学新生活不适应?送你舒心指南! 大学新生的不适应主要有哪些方面
- 11-13第一次倒班可能会让人感到有些不适应,以下是一些建议
- 11-13货物大小不同装柜算法有哪些?怎么算?区别有哪些?
- 11-13五大基本算法 五大基本算法是什么
- 11-13高级程序员必备:分治算法分享 分冶算法
- 11-13最快速的寻路算法 Jump Point Search
- 11-13手机实时人工智能之「三维动作识别」:每帧只需9ms
- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)