排序算法 --- 计数排序
前面说的那些排序算法,都是要通过比较来实现的。排序还能不通过比较来实现?是的,计数排序就是这么神奇。
一、排序思想
创建一个计数数组,利用数组下标来表示该元素,用数组下标对应的值来表示元素出现的次数。然后遍历计数数组即可。比如下标为5,元素值为2,表示5出现两次,连续写两次5即可。
欢迎大家关注我的公众号 javawebkf,目前正在慢慢地将简书文章搬到公众号,以后简书和公众号文章将同步更新,且简书上的付费文章在公众号上将免费。
1. 案例:
假如待排序列arr
如下:
5 7 4 8 3 5
最大元素是8,所以创建一个最大下标为8的数组:
int[] count = new int[9];
遍历待排序列,第一个是5
,所以count[5]++]
,第二个是7
,所以count[7]++
……
最终count数组就是:
0 0 0 1 1 2 0 1 1 // 元素值
0 1 2 3 4 5 6 7 8 // 下标
最后根据count数组,可以知道,3
出现一次,4
出现一次,5
出现两次……就可以知道排序后应该是这样的:
3 4 5 5 7 8
这样看似很完美,但是会存在两个问题。
2. 问题一:
上面的5
出现了两次,最后排完序的的数组中下标为2的那个5
,还是原序列中下标为0的那个5
吗?也就是说,当值相同的情况下,无法保证排序后相同元素出现的顺序和排序前一致,这也就是我们说的不稳定排序。如何优化呢?
我们给之前的数组中两个5
做上标记,便于区分:
小红 小白
5 7 4 8 3 5
- 然后和之前一样,统计每个元素出现的次数,得到count数组:
0 0 0 1 1 2 0 1 1 // 元素值
0 1 2 3 4 5 6 7 8 // 下标
- 接下来,对count数组进行变形,然后一个元素加上前一个元素,即:
count[i] = count[i] + count[i-1];
这样一来,count数组就变成了:
0 0 0 1 2 4 4 5 6 // 元素值
0 1 2 3 4 5 6 7 8 // 下标
- 然后,创建一个新数组
resultArr
,长度和原数组arr
一样。从后往前遍历原数组arr
,第一个是5
,标记是小白,count[5]
的值是4
,表示小白排第四位,所以resultArr[4-1] = 5
,同时count[5]--
,即把4
变成3
,下一个5
就表示排第三位,小红就排第三,和原数组的顺序一致。这样一来,就将计数排序变成稳定的了。
3. 问题二:
假如现有待排序列arr
如下:
999 998 1000 995
按照之前的说法,count
数组的最大下标是arr
数组最大值,即如果要排这四个数,需要创建长度为1001的数组。而且,下标0到994的这些空间都用不到,白白浪费了。所以,count
数组的长度应该是max(arr) - min(arr) + 1
,即用最大值减去最小值再加1即可。此案例中,count
的长度就是1000 - 995 + 1 = 6
,那么每个元素应该放在哪个下标上呢?每个元素都减去最小元素,得出来的值就对应count
的下标。比如999 - 995 = 4
,那么999
就应该对应count[4]
。
4. 计数排序的缺点: 从上面的分析可以知道,计数排序适合分布比较集中的数据,即最大值和最小值相差不多,如果相差特别多,就会很耗费空间。
二、代码实现
public static void countSort(int[] arr) {
if (arr == null || arr.length == 1) {
return;
}
// 1. 找到数组中最大的数和最小的数
int max = arr[0];
int min = arr[0];
for (int i=1; i<arr.length; i++) {
max = arr[i] > max ? arr[i] : max;
min = arr[i] < min ? arr[i] : min;
}
// 2. 定义count数组
int[] count = new int[max - min + 1];
// 3. 遍历原数组,进行计数
for (int i=0; i<arr.length; i++) {
count[arr[i] - min]++;
}
// 4. 对count数组进行变形,让计数排序变成稳定的
for (int i=1; i<count.length; i++) {
count[i] += count[i-1];
}
// 5. 创建接收结果的数组
int[] result = new int[arr.length];
// 6. 倒序遍历原数组,并且将结果存到result数组中
for (int i=arr.length-1; i>=0; i--) {
result[count[arr[i] - min] - 1] = arr[i];
count[arr[i] - min] --;
}
// 7. 把result数组拷贝回原数组即可
System.arraycopy(result, 0, arr, 0, arr.length);
}
- 讨厌算法的程序员 2 - 证明算法的正确性
- Day4上午解题报告
- [编程经验] Python正则表达式
- Day4下午解题报告
- linux下 Error running javac compiler
- 讨厌算法的程序员 1 - 插入排序
- Linux下使用ssh密钥实现无交互备份
- [编程经验] Python中的continue和break语句
- 洛谷 P3386 【模板】二分图匹配 Dinic版
- [编程经验] 拉勾网爬虫数据的后续处理
- Linux下使用rsync实现文件备份
- 【干货】基于TensorFlow卷积神经网络的短期股票预测
- [编程经验] 基于bs4的拉勾网AI相关工作爬虫实现
- [编程经验] 链家23个全国主要城市的现房数据分析
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- VBS 脚本语言-利用vbs调用ie浏览器访问百度查天气实例演示
- vbs控制电脑说话,vbs获取时间,vbs小程序
- Android 天气APP(十四)修复UI显示异常、优化业务代码逻辑、增加详情天气显示
- Android 天气APP(十五)增加城市搜索、历史搜索记录
- python 微信机器人-如何调用机器人的api,调用图灵机器人接口演示。调用机器人原理,图灵机器人注册。
- Python 技术篇-如何查看python库包含什么方法,python库有哪些用法,python库的属性。
- Android Studio 安装配置教程 - MacOS(详细版)
- Android 天气APP(十六)热门城市 - 海外城市
- Android 天气APP(十七)热门城市 - 国内城市
- Python 技术篇-PIL库安装及截图功能演示
- Android 天气APP(十八)常用城市
- Android 蓝牙开发(扫描设备、绑定、解绑)
- Python 微信机器人:调用电脑摄像头时时监控功能实现演示,调用电脑摄像头进行拍照并保存。
- Android 天气APP(十九)更换新版API接口(更高、更快、更强)
- Android 天气APP(二十)增加欢迎页及白屏黑屏处理、展示世界国家/地区的城市数据