数据结构与算法-排序(八)计数排序(Counting Sort)
摘要
计数排序本质就是统计不同元素出现的次数,然后将元素依次从小到大放置,每个元素看统计的次数,就紧挨着放置几个同样的元素。
看似简单的处理,在算法中,会依据统计的元素次数推算出每个元素的索引位置,这就是算法的魅力。
逻辑
统计每个整数在序列中出现的次数,进而推导出每个整数在有序序列中的索引。
这是通过空间换取时间的方式。
流程
- 获取序列中的最大值
- 统计每个元素出现的次数
- 按照顺序进行赋值
实现
根据获取到的最大值,创建序列统计序列中的元素出现的次数。创建的序列大小是 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 是整数的取值范围
数据结构与算法-排序(八)计数排序(Counting Sort)的更多相关文章
- Hark的数据结构与算法练习之计数排序
算法说明 计数排序属于线性排序,它的时间复杂度远远大于常用的比较排序.(计数是O(n),而比较排序不会超过O(nlog2nJ)). 其实计数排序大部分很好理解的,唯一理解起来很蛋疼的是为了保证算法稳定 ...
- JavaScript 数据结构与算法之美 - 桶排序、计数排序、基数排序
1. 前言 算法为王. 想学好前端,先练好内功,只有内功深厚者,前端之路才会走得更远. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScript ,旨在入门数据结构与算 ...
- 数据结构和算法(Golang实现)(18)排序算法-前言
排序算法 人类的发展中,我们学会了计数,比如知道小明今天打猎的兔子的数量是多少.另外一方面,我们也需要判断,今天哪个人打猎打得多,我们需要比较. 所以,排序这个很自然的需求就出来了.比如小明打了5只兔 ...
- 排序算法的C语言实现(下 线性时间排序:计数排序与基数排序)
计数排序 计数排序是一种高效的线性排序. 它通过计算一个集合中元素出现的次数来确定集合如何排序.不同于插入排序.快速排序等基于元素比较的排序,计数排序是不需要进行元素比较的,而且它的运行效率要比效率为 ...
- 数据结构和算法(Golang实现)(25)排序算法-快速排序
快速排序 快速排序是一种分治策略的排序算法,是由英国计算机科学家Tony Hoare发明的, 该算法被发布在1961年的Communications of the ACM 国际计算机学会月刊. 注:A ...
- 数据结构和算法(Golang实现)(19)排序算法-冒泡排序
冒泡排序 冒泡排序是大多数人学的第一种排序算法,在面试中,也是问的最多的一种,有时候还要求手写排序代码,因为比较简单. 冒泡排序属于交换类的排序算法. 一.算法介绍 现在有一堆乱序的数,比如:5 9 ...
- 数据结构和算法(Golang实现)(20)排序算法-选择排序
选择排序 选择排序,一般我们指的是简单选择排序,也可以叫直接选择排序,它不像冒泡排序一样相邻地交换元素,而是通过选择最小的元素,每轮迭代只需交换一次.虽然交换次数比冒泡少很多,但效率和冒泡排序一样的糟 ...
- 数据结构和算法(Golang实现)(21)排序算法-插入排序
插入排序 插入排序,一般我们指的是简单插入排序,也可以叫直接插入排序.就是说,每次把一个数插到已经排好序的数列里面形成新的排好序的数列,以此反复. 插入排序属于插入类排序算法. 除了我以外,有些人打扑 ...
- 数据结构和算法(Golang实现)(22)排序算法-希尔排序
希尔排序 1959 年一个叫Donald L. Shell (March 1, 1924 – November 2, 2015)的美国人在Communications of the ACM 国际计算机 ...
- 数据结构和算法(Golang实现)(23)排序算法-归并排序
归并排序 归并排序是一种分治策略的排序算法.它是一种比较特殊的排序算法,通过递归地先使每个子序列有序,再将两个有序的序列进行合并成一个有序的序列. 归并排序首先由著名的现代计算机之父John_von_ ...
随机推荐
- XCTF(MISC) 图片隐写
题目描述:菜猫给了菜狗一张图,说图下面什么都没有 1.给了个pdf,打开是这玩意 2.盲猜flag是图片后面,右键直接删除图片试试. 答案出来了.
- SA20225394 舒蔚 高级软件工程实验总结
[实验]: 一.编程神器Visual Studio Code 配置好Visual Studio Code这一强大而又轻量的编辑器.其中有版本控制+代码补全+自动加载依赖,并且可以根据自己的需要添加插件 ...
- C预处理跨平台
#include <stdio.h> //不同的平台下引入不同的头文件 #if _WIN32 //识别windows平台 #include <windows.h> #elif ...
- Python自动化测试面试题-用例设计篇
目录 Python自动化测试面试题-经验篇 Python自动化测试面试题-用例设计篇 Python自动化测试面试题-Linux篇 Python自动化测试面试题-MySQL篇 Python自动化测试面试 ...
- redis数据类型及应用场景
0.key的通用操作 KEYS * keys a keys a* 查看已存在所有键的名字 ****TYPE 返回键所存储值的类型 ****EXPIRE\ PEXPIRE 以秒\毫秒设定生存时间 *** ...
- spring web.xml 标签<param-name>contextConfigLocation</param-name>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</lis ...
- (数据科学学习手札125)在Python中操纵json数据的最佳方式
本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 在日常使用Python的过程中,我们经常会 ...
- SpringBoot Aop 详解和多种使用场景
前言 aop面向切面编程,是编程中一个很重要的思想本篇文章主要介绍的是SpringBoot切面Aop的使用和案例 什么是aop AOP(Aspect OrientedProgramming):面向切面 ...
- js学习笔记之公告逐行显示
$(function(){ var newsListHeight = $(".news-list").height(); //获得内容的高度 var newsConHeight = ...
- 大数据学习(06)——Ozone介绍
前面几篇文章把Hadoop常用的模块都学习了,剩下一个新模块Ozone,截止到今天最新版本是0.5.0Beta,还没出正式版.好在官方网站有文档,还是中文版的,但是中文版资料没有翻译完整,我试着把它都 ...