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

计数排序

计数排序由 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;再指示舵机运…

796. 子矩阵的和

Problem: 796. 子矩阵的和 文章目录 思路解题方法复杂度Code 思路 这是一个二维前缀和的问题。二维前缀和的主要思想是预处理出一个二维数组&#xff0c;使得每个位置(i, j)上的值表示原数组中从(0, 0)到(i, j)形成的子矩阵中所有元素的和。这样&#xff0c;对于任意的子矩阵(x…

MySQL数据库应用实验报告——实验1 表结构创建

实验1 表结构创建 创建用于大学管理的高校管理数据库&#xff0c;数据库名为GXGL&#xff0c;包含学生的信息&#xff0c;教学单位信 息、专业信息&#xff0c;教职工信息、课程的相关信息以及学生选课信息。数据库GXGL包含下列 六个表: (1) Students: 学生信息表 (2) Depar…

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主&…

基于设计模式,实现分布式锁的资源管理

org.redisson.api.RLock&#xff0c;是目前较为常见的分部署锁实现方式。我们的目的是实现自动管理锁的获取和释放。 但遗憾的是&#xff0c;RLock并不实现AutoCloseable接口&#xff0c;因此不能直接用在try-with-resources结构中。不过&#xff0c;我们可以通过创建一个包装类…

【RISC-V DSP设计】基于CEVA DSP架构的指令集分析(一)-总体介绍

目录 一、引言 二、CEVA-BX1™ DSP Library 概述 三、CEVA-BX1™ DSP Library 功能与特点 四、CEVA-BX1™ DSP Library 优势 今天开始我们继续对CEVA DSP的架构和指令集进行分析&#xff0c;基于对CEVA DSP的分析和了解&#xff0c;后续可以进行基于RISC-V内核架构的DSP指令…

【Python】洛谷P4325 [COCI2006-2007#1] Modulo

P4325 [COCI2006-2007#1] Modulo 题面翻译 给出 10 10 10 个整数&#xff0c;问这些整数除以 42 42 42 后得到的余数有多少种。 第一个样例的十个结果是 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 1,2,3,4,5,6,7,8,9,10 1,2,3,4,5,6,7,8,9,10&#xff0c;有 10 10 10 个不…

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

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

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

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

ES实战--wildcard正则匹配exists过滤字段是否存在

wildcard 通配符中的 * 表示任意数量的字符 ?表示任意单个字符 #正则匹配 GET /wildcard-test/_search {"query": {"wildcard": {"title": {"wildcard": "ba*n"}}} } #响应:"hits": {"total": {"…

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…

为自己的项目媒体资源添加固定高度

为自己的项目媒体资源添加固定高度 未媒体资源添加固定高度&#xff0c;不仅有利于确定懒加载后的切确位置&#xff0c;还可以做骨架屏、loading动画等等&#xff0c;但是因为历史数据中很多没有加高度的媒体资源&#xff0c;所以一直嫌麻烦没有做。 直到这个季度有一个自上而…

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

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