线性时间非比较类排序之计数排序

计数排序

计数排序由 HaroldH.Seward 于1954年提出,它是一种非基于比较的排序算法,通过辅助数组来确定各元素的最终位置。因为在排序过程中不存在元素之间的比较和交换操作,所以当待排序数组为整数且数组内数据的范围较小时,其优势是十分明显的。

1. 算法思想

对待排序数组中的每一个元素分别进行计数,确定整个数组中小于当前元素的个数,计数完成后便可以按照各计数值将各元素直接存放在已排序的数组中。

注意:当存在多个相同的值时,不可以把它们放在同一位置上。需要在代码中进行适当的修改。

2. 算法步骤

(1)根据待排序序列中的最大元素和最小元素确定计数数组c的空间大小。
(2) 统计待排序序列中每个元素i出现的次数并存入c[i-1]中。
(3)对c中所有的值进行累加,后者为其前面所有的值的总和。
(4)将每个元素放入已排序序列的第c[i]项,每放入一个元素,c[i]便减1。
(5)所有的元素都放入或者c[i]均为0之后,排序结束。

3. 算法分析

第一个循环用于记录每个元素出现的次数,复杂度为 O(n);第二个循环用于对计数数组进行累加,复杂度为O(k),k为申请空间的大小(即差值范围):第三个循环用于反向填充已排序序列,复杂度为O(n)。总结可得,计数排序的时间复杂度为O(n+k)。
正如前文所说的“以空间换时间”——虽然十分消耗空间,但只要 O ( k ) < O ( n l o g 2 n ) O(k)<O(nlog_2n) O(k)<O(nlog2n),计数排序便可快于任何比较排序算法。因比较排序算法的时间复杂度存在一个理论边界—— O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
在比较排序算法中,如果存在n个元素,则会形成n!个排列个数,也就是说这个序列构成的决策树叶子节点个数为n!。由叶子节点的个数又可以知道,这棵决策树的高度为 l o g 2 ( n ! ) log_2(n!) log2(n!),也就是说从这棵树的根节点到叶子节点需要比较 l o g 2 ( n ! ) log_2(n!) log2(n!)次。根据斯特灵公式,已知:
n ! ≈ 2 π n { n e } n n!\approx\sqrt{2\pi n}\{\frac{n}{e}\}^n n!2πn {en}n
所以:
O { l o g 2 ( n ! ) } ≈ O { l o g 2 { 2 π n { n e n } } ≈ O ( n l o g 2 n ) O\{log_2(n!)\}\approx O\{log_2\{\sqrt{2\pi n}\{\frac{n}{e}^n\}\}\approx O(nlog_2n) O{log2(n!)}O{log2{2πn {enn}}O(nlog2n)

注意:如果 O ( k ) < O ( n l o g 2 n ) O(k)<O(nlog_2 n) O(k)<O(nlog2n),那么其反而不如基于比较的排序算法(如快速排序、堆排序和归并排序)快。

由于我们额外申请了一个大小为k的计数数组和一个与待排序数组同样大小的排序空间,所以空间复杂度为 O(n+k),而且计数排序属于稳定排序。
结合时间复杂度和空间复杂度不难发现计数排序存在的弊端:待排序数组中的最大值和最小值之差不可太大,否则会使开销增大,性能变差;因为我们是以元素值为下标,所以在待排序数组中最好不要存在浮点数或其他形式的元素,否则还需要对元素值和元素差值进行相应的转换。因此,一定要谨慎使用计数排序。

4. 算法代码

算法代码如下:
Python

# -*- coding: utf-8 -*-# 计数排序
def count_sort(arr):# 计算序列的最大值和最小值maximum, minimum = max(arr),min(arr)# 申请额外的空间作为计数数组,大小为最大值减最小值+1countArr = [0 for i in range(maximum - minimum + 1)]# 申请一个存放已排序序列的等长空间finalArr =[0 for i in range(len(arr))]# 统计各元素出现的次数,并将统计结果存入计数数组中for i in arr:# 出现一次就加 1countArr[i-minimum]+= 1  # 注意下标 index = data - min# 对计数数组进行累加for i in range(1, len(countArr)):# 从第二个位置开始,每个位置的值等于其本身加上前身的值countArr[i]+= countArr[i-1]# 开始把元素反向填充至最终的数组中for i in arr:finalArr[countArr [i-minimum]-1] = i#填充了一个就减一countArr[i-minimum] -= 1return finalArr
#调用count_sort 函数
print(count_sort([34,21,-2, 13,2,5,1,55,-3,3,1,8]))

Java

  public static List<Integer> countSort(List<Integer> arr) {// 计算序列的最大值和最小值int maxPositive = Integer.MIN_VALUE, minNegative = Integer.MAX_VALUE;for (int i : arr) {if (i > 0) {maxPositive = Math.max(maxPositive, i);} else if (i < 0) {minNegative = Math.min(minNegative, i);}}// 分别申请额外的空间作为正数计数数组、负数计数数组和临时存储列表int positiveRange = maxPositive + 1;int[] positiveCountArr = new int[positiveRange];Arrays.fill(positiveCountArr, 0);int negativeRange = Math.abs(minNegative) + 1;int[] negativeCountArr = new int[negativeRange];Arrays.fill(negativeCountArr, 0);// 统计各元素出现的次数,并将统计结果存入相应的计数数组中for (int num : arr) {if (num >= 0) {positiveCountArr[num]++;} else {negativeCountArr[Math.abs(num)]++;}}// 开始把元素反向填充至最终的数组中List<Integer> finalArr = new ArrayList<>(arr.size());// 先添加负数for (int i = negativeCountArr.length - 1; i >= 1; i--) {while (negativeCountArr[i]-- > 0) {finalArr.add(-i);}}// 添加零(如果有的话)if (positiveCountArr[0] > 0) {while (positiveCountArr[0]-- > 0) {finalArr.add(0);}}// 添加正数for (int i = 1; i < positiveCountArr.length; i++) {while (positiveCountArr[i]-- > 0) {finalArr.add(i);}}return finalArr;}@Testvoid contextLoads () {List<Integer> arr = List.of(34, 21, -2, 13, 2, 5, 1, 55, -3, 3, 1, 8);List<Integer> sortedArr = countSort(arr);System.out.println(sortedArr);}

5. 输出结果

代码输出结果如下:
在这里插入图片描述

6. 算法过程分解

待排序序列[-2,2,5,1,-3,3,1]
最大值 5,最小值为-3
所以,需申请的计数数组空间为9
计数数组如下:
在这里插入图片描述
累加更新后,计数数组如下;
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/680445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算x的平方根x含负数和复数cmath.sqrt(x)

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算x的平方根 x含负数和复数 cmath.sqrt(x) cmath.sqrt(-4)输出的结果是&#xff1f; import cmath import math a 4 print("【显示】a ",a) print("【执行】math.sqrt(a)&…

有状态DHCPv6快速模式配置及EUI-64介绍

正文共&#xff1a;1024 字 15 图&#xff0c;预估阅读时间&#xff1a;3 分钟 我们现在已经熟悉了IPv6的地址架构&#xff08;IPv6地址架构一本通&#xff09;&#xff0c;掌握了IPv6地址的手工配置方式&#xff08;IPv6从入门到精通&#xff09;和DHCPv6有状态地址配置&#…

openssl3.2 - osslsigncode工程的学习

文章目录 openssl3.2 - osslsigncode工程的学习概述笔记工程库地址工程的编译osslsigncodeM工程文件列表osslsigncodeM工程搭建细节原始工程实现的改动自己封装的包含openssl和curl的实现osslsigncodeM工程命令行的用法备注 - VS2019调试环境备注 - 如果要单步openssl的API学学…

第六篇:MySQL图形化管理工具

经过前五篇的学习&#xff0c;对于数据库这门技术的理解&#xff0c;我们已经在心中建立了一个城堡大致的雏形&#xff0c;通过命令行窗口&#xff08;cmd&#xff09;快速上手了【SQL语法-DDL-数据定义语言】等相关命令 道阻且长&#xff0c;数据库技术这一宝藏中还有数不清的…

人脸追踪案例及机器学习认识

1.人脸追踪机器人初制 用程序控制舵机运动的方法与机械臂项目完全相同。 由于摄像头的安装方式为上下倒转安装&#xff0c;我们在编写程序读取图像时需使用 flip 函数将 图像上下翻转。 现在&#xff0c;只需要使用哈尔特征检测得到人脸在图像中的位置&#xff0c;再指示舵机运…

N1CTF奖品一个月的ZoomEye账户使用与子域名收集(网络渗透测)

首页 - 网络空间测绘,网络安全,漏洞分析,动态测绘,钟馗之眼,时空测绘,赛博测绘 - ZoomEye("钟馗之眼")网络空间搜索引擎https://www.zoomeye.org/ZoomEye - Cyberspace Search Enginehttps://www.zoomeye.org/aboutZoomEye&#xff08;“钟馗之眼”&#xff09;是知道…

幻兽帕鲁游戏官方更新了版本,联机时提示版本不适用,无法加入,怎么办?

如果你在登录游戏的时候提示&#xff1a;您正在尝试加入的比赛正在运行不兼容的游戏版本。请尝试升级游戏版本。此时就说明你需要更新部署在服务器内的幻兽帕鲁了。 1、如果你使用幻兽帕鲁应用模板部署游戏&#xff0c;那么可以选择使用游戏配置面板一键更新。 2、如果你使用一…

Day46 300最长递增子序列 674最长连续递增子序列 718最长重复子数组 1143最长公共子序列

300 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序…

《UE5_C++多人TPS完整教程》学习笔记7 ——《P8 为项目配置 Steam(Configuring A Project for Steam)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P8 为项目配置 Steam&#xff08;Configuring A Project for Steam&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&…

软件架构与系统架构:区别与联系的分析

软件架构与系统架构&#xff1a;区别与联系的分析 在信息技术领域&#xff0c;软件架构和系统架构这两个术语经常被提及。尽管它们在某些方面有重叠&#xff0c;但它们确实代表了不同的概念和聚焦点。理解这两种架构之间的区别和联系对于任何从事技术开发和设计的专业人士都是至…

【C语言】指针专项练习 都是一些大厂的笔试真题 附有详细解析,带你深入理解指针

一.sizeof()与strlen() sizeof是一个操作符&#xff0c;而strlen是一个库函数。 数组名代表首元素地址&#xff0c;有两种情况例外&#xff0c;第一种是数组名单独放在sizeof内部&#xff0c;第二种是&数组名&#xff0c;这两种情况下数组名代表的是整个数组。sizeof(arr…

Vue + Element UI el-table + sortablejs 行、列拖拽排序

实现Element UI中的el-table表格组件的行和列的拖拽排序 使用 Vue3 Element Plus UI sortablejs 安装sortablejs pnpm install sortablejs行拖拽 基本实现 效果 <script setup> import { onMounted, ref } from "vue"; import Sortable from "sort…

蓝桥杯每日一练(python)B组

###来源于dotcpp的蓝桥杯真题 题目 2735: 蓝桥杯2022年第十三届决赛真题-取模&#xff08;Python组&#xff09; 给定 n, m &#xff0c;问是否存在两个不同的数 x, y 使得 1 ≤ x < y ≤ m 且 n mod x n mod y 。 输入格式&#xff1a; 输入包含多组独立的询问。 第一…

关于在分布式环境中RVN和使用场景的介绍3

简介 在《关于在分布式环境中RVN和使用场景的介绍2》和《关于在分布式环境中RVN和使用场景的介绍1》中我们介绍了RVN的概念和在一些具体用例中的使用。在本文中我们讨论一下在分布式环境中使用RVN需要注意的问题。 问题 我们在收到一条待处理的事件时&#xff0c;需要检查该…

绝大部分人都不知道如何鉴定Oracle OCP/OCM和MySQL OCP证书的真伪

知道如何鉴定自己的Oracle OCP/OCM和MySQL OCP证书的真伪很重要&#xff0c;因为目前的IT证书基本都是电子的&#xff0c;很少有纸质的证书。如果要验证这些电子证书的真伪&#xff0c;通常可以到发证机构的网站输入证书ID号进行查询。Oracle公司的Oracle和MySQL数据库的OCP/OC…

springboot173疫苗发布和接种预约系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

FT2232调试记录(1)

FT2232调试记录 &#xff08;1&#xff09;获取当前连接的FTDI设备通道个数:&#xff08;2&#xff09;获取当前连接的设备通道的信息:&#xff08;3&#xff09;配置SPI的通道:&#xff08;4&#xff09;如何设置GPIO:&#xff08;5&#xff09;DEMO测试&#xff1a; #参考文档…

全坚固平板EM-I12U,全新升级后的优质体验

平板终端机在户外勘探、制造业、畜牧业、银行金融行业当中都不是陌生的&#xff0c;能采集各种数据来转换成信息流向企业和行业的各个分支当中&#xff0c;在整个行业发展、社会推动上面都起着关键性作用&#xff0c;而平板终端机的升级也就意味着未来的这些行业发展会进入一个…

闲来无事,写几个好看的产品宣传界面,希望您喜欢

闲来无事&#xff0c;再写几个产品宣传页 宣传页面一&#xff1a; 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&…