计数排序(counting sort)

ops/2024/10/18 16:02:02/

计算排序是一种通过计算每个元素出现的次数来进行排序的算法。它不比较元素,而是利用数组来“计数”,所以在某些情况下,它能比传统的比较排序快。

计数排序首先要找到要排序数组的最大值,然后创建一个统计数组存储每个排序元素的出现次数,然后累加计数数组,根据计数数组来确定每个元素的位置。

以一个实际例子来看,假设待排序数组:{2,8,7,6,4,5,2,8,5}

统计每个元素出现次数

要记录元素的统计次数,这里要创建一个统计数组用来记录次数,由于要记录每个元素的次数,所以要先找到元素的最大值来确定统计数组的容量,这里最大值是8,所以要创建一个 8+1长度的统计数组来存储元素记录数。考虑元素为0的情况。

最后统计数组结果如下:

002012112
下标012345678

这里统计数组下标待排序元素值,数组中值代表该元素出现次数

累加统计数组

这里累加就是将统计数组总的当前位置索引值改为前面所有统计数的和。

这一步其实就是确定每个元素的大致位置,只不过由于有相同的元素还需要进行微调。

这里从统计数组头开始具体看,其实可以理解成排名次,首先 0和1出现次数都是0直接舍弃,接着2出现了两次,前两名就是2个2,3

下标说明累加值
0次数是0,舍弃0
1次数是0,舍弃0
2次数是2,则这两个2(下标2)占据前两名2
3次数是0,舍弃2
4次数是1,一个4排第三名3
5次数2,两个5排占据4,5名5
6次数1,一个6排第6名6
7次数1,一个7排第7名7
8次数2,两个8占据8、9名9

确定元素位置

上面计数数组累加完成后,大致顺序已经出来了,只不过有相同元素存在导致元素具体位置存在空档和重复。

最后元素排序后的位置需要定义一个新的数组来存储,元素的位置还是

这一步就需要根据统计数组中的累加值来计算,计数数组中的累加值当对应位置元素被拿出一次后,当前位置累加数-1,便于下次在取的时候位置错开。

原数组 {2,8,7,6,4,5,2,8,5}

一次根据计数类加数确定位置

原数组元素计数数组累加值变化输出数组位置说明
22->12
89->89第一次拿出计数减去1
77->67
66->56
43->23
55->45
21->01第二次拿出在第一次基础上计算
88->78第二次拿出在第一次基础上计算
54->34第二次拿出在第一次基础上计算

最后确定每个元素在输出数组中的位置,排序也完成了。

代码实现

java">public static int[] sort(int[] arr){//1、找到数组最大值int max = arr[0];for (int i = 1; i < arr.length; i++) {if(arr[i] > max) max = arr[i];}//2、创建计数数组,数组长度 max+1,要存储所有可能元素计数,+1是考虑有元素0的情况int[] countArr = new int[max+1];//初始化for (int i = 0; i < countArr.length; i++) {countArr[i] = 0;}/*** 元素计数,这里 countArr:下标是排序数组元素,值是当前这个数出现的次数*/for (int i = 0; i < arr.length; i++) {countArr[arr[i] ] += 1;}/*** 累加计数 ,这里是将计数数组的值依次累加,这样就能知道每个元素(排序数组下标)对应的排序位置*/for (int i = 1; i < countArr.length; i++) {countArr[i] += countArr[i-1];}/*** 构建输出数组* 根据当前元素累加值确定输出数组位置,每次拿出一个,原来对应位置累加值-1*/int[] outArr = new int[arr.length];for (int i = 0; i < arr.length; i++) {outArr[ countArr[arr[i]]-1 ] = arr[i];countArr[arr[i]]--;}return outArr;
}

计数排序巧妙的运用了一个计数数组,每个元素值作为计数数组的下标来存取值。上面排序过程中也看到虽然没有进行排序比较,但是过程中创建了两个数组来存储计数和输出结果,空间复杂度提高。是一种用空间换时间的排序方式。

计算排序适合用于数组元素范围不大(例如 0 到 1000)。对于大量具有相同值的元素的排序。值的范围太大会造成计数数组空间偏大。如果排序中元素最小值较大,计数数组长度可以取为[最小值,最大值],元素下标统一偏移最小值即可。这样节省了最小值个数组长度空间。


http://www.ppmy.cn/ops/121966.html

相关文章

rk3566开发之rknn npu 部署

目录 NPU使用 RKNN 模型 非 RKNN 模型 RKNN-Toolkit2工具 RKNN NPU 测试代码如下 main.cc ssd.cc 调用 ssd模型进行目标检测测试 ssd.h qt 中调用 rknn npu 接口 NPU使用 RK3566 内置 NPU 模块。使用该NPU需要下载RKNN SDK,RKNN SDK 为带有 NPU 的 RK3566/RK3568 芯片…

【数据结构】【链表代码】移除链表元素

移除链表元素 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* removeElements(struct ListNode* head, int val) { // 创建一个虚拟头节点&#xff0c;以处理头节点可能被删除的情况 struct…

WPF后台创建控件和绑定

WPF后台创建控件和绑定 一、创建一个类&#xff1a; public class MyData : INotifyPropertyChanged {private string text;public string Text{get { return text; }set { text value;OnPropertyChanged(nameof(Text));}}public event PropertyChangedEventHandler Propert…

蓝桥杯省赛真题打卡day4

[蓝桥杯 2013 省 A] 大臣的旅费 题目描述 很久以前&#xff0c;T 王国空前繁荣。为了更好地管理国家&#xff0c;王国修建了大量的快速路&#xff0c;用于连接首都和王国内的各大城市。 为节省经费&#xff0c;T 国的大臣们经过思考&#xff0c;制定了一套优秀的修建方案&am…

c基础面试题

1.static和const的作用 static意为静态的&#xff0c;在C语言中可以修饰变量。如果是全局变量则只能在当前文件范围访问。 如果是函数内的局部变量则延长生命周期到整个程序。这意味着如果函数被多次调用&#xff0c;这个变量不会被重新初始化&#xff0c;而是保留上次调用结…

Java中数据转换以及字符串的“+”操作

隐式转换&#xff08;自动类型转换&#xff09; 较小范围的数据类型转成较大范围的数据类型 强制转换&#xff08;显式转换&#xff09; 将数据范围大的数据类型转换为数据范围小的数据类型 基本数据类型之间的转换 当需要将一个较大的数据类型&#xff08;如float或double…

从WIFI到NB-IoT,探秘智能门锁的高科技接入方式

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! Hello大家好!我是小米,一个29岁、活力满满、热爱分享技术的小米!今天,我想和大家聊聊一个与智能家居密切相关的技术话题——智能门锁的接入方式。无…

2024年OpenAI DevDay发布实时 API、提示缓存等新功能

就在几天前&#xff0c;一些重要人物如前 CTO Mira Murati 离开了 OpenAI。因此&#xff0c;看到 Sam Altman 在 DevDay 上登台&#xff0c;讨论开发者的新产品&#xff0c;感觉有点奇怪。 随着公司内部的这些变化&#xff0c;你不禁会想&#xff1a;我们还应该信任他吗&#…