计数排序与基数排序

news2024/12/26 23:45:51

计数排序与基数排序

计数排序

计数排序:使用一个数组记录序列中每一个数字出现的次数,将该数组的下标作为实际数据,元素的值作为数据出现的次数。例如对于序列[3,0,1,1,3,3,0,2],统计的结果为:

0出现的次数:2次
1出现的次数:2次
2出现的次数:1次
3出现的次数:3次

依据出现的次数就可以构建出排序之后的序列

void CountSort(vector<int>& nums) {
    int minNum = nums.front();
    int maxNum = nums.front();
    for (int num : nums) {
        minNum = std::min(minNum, num);
        maxNum = std::max(maxNum, num);
    }
    int helpSize = maxNum - minNum + 1;
    int* help = new int[helpSize] {0};//例如最大值为10,最小值为5,需要开辟的数组大小是10-5+1
    for (int num : nums) {
        help[num - minNum]++;//统计次数
    }
    nums.clear();
    for (int i = 0; i < helpSize; i++) {
        while (help[i]--) {
            nums.push_back(i + minNum);
        }
    }
    delete[] help;
}

计数排序的时间复杂度和空间复杂度

时间复杂度:O(N+max-min),其中max为数组中的最大元素,min为数组中的最小元素。计数排序需要遍历原数组一趟,得到原数组的最大值和最小值从而决定需要开辟的辅助数组的大小,还需要遍历辅助数组得到有序序列

空间复杂度:O(max-min),取决于数组中最大值和最小值的差

计数排序适用于数据量很大,但是数据分布比较密集的场景,例如有1亿个数,它们都在[20,1000]范围,此时就可以使用计数排序,与其它排序算法相比(例如快排、冒泡),计数排序是一种非比较排序

基数排序

在计数排序中,对于数据较为分散的场景,所需开辟的额外空间较大,例如序列[1,56,26,9999,7],若使用计数排序,需要额外开辟一个大小为9999的数组,但是原数组只有5个数需要进行排序,在这种情况下,可以考虑使用基数排序

基数排序也是一种非比较排序,基本思想是:先将原序列按照个位进行排序,在按照十位进行排序……依次类推,直到序列完全有序,以[1,56,26,9999,7]为例,流程如下:

在这里插入图片描述

基数排序的轮数取决于序列中最大值的位数,在进行基数排序时,位数小的数字一定在位数大的数字的前面,例如上图中7虽然在进行第一轮排序完成后处于56的后面,但是当完成第二轮排序后7处于56的前面,因为7的十位为0

如何提取一个数字的个位、十位、百位?

方案一:使用to_string将该数字转化为字符串,依次进行提取

方案二:定义一个offset变量,初始为1,将(num/offset)%10,得到个位,每进行一轮,将offset*10

int num = 3675;
int offset = 1;
int bitNum;
while (bitNum=num / offset) {
    cout << bitNum % 10 << ' ';
    offset *= 10;
}
cout << endl;

基数排序的桶

在进行基数排序时一般使用队列作为基数排序的桶,对10进制数字进行排序就需要准备9个队列

void RadixSortByQueue(vector<int>& nums, int bits, int BASE = 10) {//使用队列作为桶进行基数排序
    //bits表示序列中的最大值的位数
    //BASE表示多少进制
    vector<queue<int>> Queues;
    Queues.resize(BASE);
    int offset = 1;
    for (int offset = 1; bits > 0; bits--, offset *= BASE) {//例如最大值为156,则进行3轮
        for (int num : nums) {
            int bitNum = (num / offset) % BASE;//得到个位/十位/百位……的数
            Queues[bitNum].push(num);
        }
        //按照个位/十位/百位……排好的数已经放入Queues中
        nums.clear();
        for (auto& Queue : Queues) {
            while (!Queue.empty()) {
                nums.push_back(Queue.front());
                Queue.pop();
            }
        }
    }
}

基数排序的优化

前缀数量分区:以序列[1,56,26,9999,7]为例,个位数分别是1,6,6,9,7,可以得到的前缀信息如下

个位数<=1的数据数量:1
个位数<=6的数据数量:3
个位数<=7的数据数量:4
个位数<=9的数据数量:5

那么在每一轮按照特定位进行排序时就不需要使用队列,直接开一个和原始数组等规模的辅助数组help即可,将[1,56,26,9999,7]按照个位进行排序,对于数字7,个位为7,由于个位数<=7的数据数量是4,所以7直接放到help数组中下标为3的位置,此时原序列中的7已经放到help数组中,原序列中个位数<=7的数据数量减少一个,变为3,以此类推,直到原序列中的数据全部按照规则转移到help数组中,此时help数组中的数据就是按照位排序好的数据

void RadixSort(vector<int>& nums, int bits, int BASE = 10) {
    //bits表示序列中的最大值的位数
    //BASE表示多少进制
    int* counts = new int[BASE] {0};
    int* help = new int[nums.size()]{ 0 };
    int offset = 1;
    for (int offset = 1; bits > 0; bits--, offset *= BASE) {
        memset(counts, 0, sizeof(int) * BASE);
        for (int num : nums) {
            int bitNum = (num / offset) % BASE;
            counts[bitNum]++;//先统计个数
        }
        for (int i = 1; i < BASE; i++) {
            counts[i] += counts[i - 1];//统计前缀数量
        }
        for (int i = nums.size() - 1; i >= 0; i--) {
            int bitNum = (nums[i] / offset) % BASE;
            help[--counts[bitNum]] = nums[i];
        }
        memcpy(nums.data(), help, sizeof(int) * nums.size());
    }
    delete[] help;
    delete[] counts;
}

为什么需要从后往前遍历向help数组中填数据?

以[1,99,7]为例,在排个位数时,可以从后往前,也可以从前往后,没有影响,个位数排完之后,得到[1,7,99],但是在排十位数时,必须从后往前,否则就会打乱1和7的顺序,因为1和7的十位都是0,十位<=0的数的个数是2,7是最后一个十位<=的数,因此在遍历时需要从后往前遍历排到靠后位置.

基数排序的拓展

如果原序列中存在负数,如何进行基数排序?

将原序列中的所有数字加上最小值的绝对值,在进行基数排序,将排完序的结果在减去原来最小值的绝对值。如果存在溢出问题,需要考虑使用long long类型。

void RadixSortContainMinus(vector<int>& nums, int BASE = 10) {
    int minNum = nums[0];
    for (int num : nums) {
        minNum = std::min(minNum, num);
    }
    int maxNum = 0;
    for (int& num : nums) {
        num -= minNum;
        maxNum = std::max(maxNum, num);
    }
    int bits = 1;
    while (maxNum / BASE) {
        bits++;
        maxNum /= BASE;
    }
    RadixSort(nums, bits, BASE);
    for (int& num : nums) {
        num += minNum;
    }
}

如果需要排序的数字不是十进制,如何使用基数排序实现?

若需要排序的数字不是10进制,只需要修改BASE即可,其它思路一致,例如需要排序的数字是16进制,那么counts数组的大小定为16即可,统计每一位在0~f的数量,依然使用前缀分区技巧

基数排序的时间复杂度

基数排序的时间复杂度为O(m*n),其中m表示原序列中最大值的位数,n表示数据量,因为要根据位数确定排多少轮。基数排序的空间复杂度为O(m+n),需要使用一个help数组和一个counts数组,其中counts数组用于统计个数,help数组用于进行保存这一轮排序完毕的数据.

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

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

相关文章

【Linux】基础IO,软硬链接,动静态库

1. 认识IO 什么是IO I/O简单来说对应的就是两个单词Input和Output&#xff0c;指的是计算机系统与外部环境&#xff08;通常是硬件设备或其他计算机系统&#xff09;之间的数据交换过程 I/O 可以分为两种主要类型&#xff1a; 输入&#xff08;Input&#xff09;&#xff1a; …

生命周期简化idea配置

生命周期简图 扩展接口介绍 2.1 Aware接口 在spring中Aware接口表示的是感知接口&#xff0c;表示spring框架在Bean实例化过程中以回调的方式将特定在资源注入到Bean中去&#xff08;如&#xff1a;ApplicationContext, BeanName,BeanFactory等等&#xff09;。Aware接口本事没…

Java——break、continue(学习笔记)

1.break(主要与switch搭配使用) 在任何循环语句的主体部分&#xff0c;均可用break控制循环的流程。break用于强行退出循环&#xff0c;不执行循环中剩余的语句。 2.continue 用在循环语句体中&#xff0c;用于终止某次循环过程&#xff0c;即跳过循环体中尚未执行的语句&am…

u盘内容防止复制(U盘内数据防拷贝的方法)

随着科技的发展&#xff0c;U盘已经成为我们日常生活和工作中不可或缺的一部分。然而&#xff0c;U盘的普及也带来了一些问题&#xff0c;如数据泄露、病毒传播等。因此&#xff0c;保护U盘中的数据安全变得尤为重要。 方法一&#xff1a;设置文件权限 打开U盘&#xff0c;找到…

轻量级c语言开源日志库log.c介绍 - 实现不同级别和参数化日志打印

前言 c语言没有现成的日志库&#xff0c;如果要记录日志&#xff0c;需要自己封装一个日志库。如果要实现日志级别和参数打印&#xff0c;还是比较麻烦的&#xff0c;正好在github找到了一个c语言开源日志库&#xff0c;可以实现日志级别打印&#xff0c;参数打印&#xff0c;…

LVGL移植win端模拟显示流畅解决方案-使用 SquareLine 生成前端 UI 文件

lvgl_port_win_vscode 在 win 平台对 lvgl 方便的进行模拟显示&#xff0c;程序文件结构清晰&#xff0c;lvgl with SDL2&#xff0c;cmake 构建&#xff0c;VsCode 一键运行&#xff0c;使用 SquareLine 生成前端 UI 文件&#xff0c;win 上直接跑。 相比官方的 lvgl 移植到…

一百七十九、Linux——Linux报错No package epel-release available

一、目的 在Linux中配置Xmanager服务时&#xff0c;执行脚本时Linux报错No package epel-release available 二、解决措施 &#xff08;一&#xff09;第一步&#xff0c;# wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm &#xff08;二&…

@Validated 和 @Valid 的区别,你真的懂吗?SpringBoot 参数校验必知必会!

概述 Valid是使用Hibernate validation的时候使用Validated是只用Spring Validator校验机制使用 说明&#xff1a;java的JSR303声明了Valid这类接口&#xff0c;而Hibernate-validator对其进行了实现 Validation对Valid进行了二次封装&#xff0c;在使用上并没有区别&#xff…

水一下文章

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

zookeeper —— 分布式服务协调框架

zookeeper —— 分布式服务协调框架 一、Zookeeper概述1、Zookeeper的基本概念2、Zookeeper的特点3、Zookeeper的数据结构 二、Zookeeper的安装部署1、Zookeeper的下载2、Zookeeper的安装本地模式&#xff08;单机模式standalone&#xff09;安装部署分布式&#xff08;集群模式…

视频监控系统/视频汇聚平台EasyCVR对国标类型编码进行判断的实现方式

视频监控平台/视频存储/视频分析平台EasyCVR基于云边端一体化管理&#xff0c;支持多类型设备、多协议方式接入&#xff0c;具体包括&#xff1a;国标GB28181协议、RTMP、RTSP/Onvif、海康Ehome&#xff0c;以及海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石SDK等&#x…

WebGL 计算点光源下的漫反射光颜色

目录 点光源光 逐顶点光照&#xff08;插值&#xff09; 示例程序&#xff08;PointLightedCube.js&#xff09; 代码详解 示例效果 逐顶点处理点光源光照效果时出现的不自然现象 更逼真&#xff1a;逐片元光照 示例程序&#xff08;PointLightedCube_perFragment.js…

paddlespeech asr脚本demo

概述 paddlespeech是百度飞桨平台的开源工具包&#xff0c;主要用于语音和音频的分析处理&#xff0c;其中包含多个可选模型&#xff0c;提供语音识别、语音合成、说话人验证、关键词识别、音频分类和语音翻译等功能。 本文介绍利用ps中的asr功能实现批量处理音频文件的demo。…

回溯算法 解题思路

文章目录 算法介绍回溯算法能解决的问题解题模板1. 组合问题2. N皇后问题 算法介绍 回溯法&#xff08;Back Tracking Method&#xff09;&#xff08;探索与回溯法&#xff09;是一种选优搜索法&#xff0c;又称为试探法&#xff0c;按选优条件向前搜索&#xff0c;以达到目标…

URL 管理器

基本介绍 对外接口 对外提供两个接口&#xff1a;一个可以提取URL&#xff0c;一个可以增加URL&#xff0c;分别对应图上的1和2。 当要爬取某个网页时&#xff0c;则可以从1接口提取出该网页的URL进行爬取。 有时候爬取的网页内容中会包含别的网页链接&#xff0c;即包含有U…

java版Spring Cloud+Mybatis+Oauth2+分布式+微服务+实现工程管理系统

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管…

Sui zkLogin让真正链接10亿用户成为可能

近日&#xff0c;Sui宣布推出zkLogin&#xff0c;这是将用户引入链上的最简单方式。zkLogin是Sui的一种原生功能&#xff0c;允许用户使用来自Google和Twitch等现有的Web2身份验证登录Web3应用程序&#xff0c;消除了用户需要记住或记录私钥的流程。 创建钱包通常被认为是区块…

使用vite创建vue3项目及项目的配置 | 环境准备 ESLint配置 prettier配置 husky配置 项目继承

文章目录 使用vite创建vue3项目及项目的配置1.环境准备2.项目配置ESLint校验代码工具配置 - js代码检测工具1.安装ESLint到开发环境 devDependencies2.生成配置文件:.eslint.cjs**3.安装vue3环境代码校验插件**4. 修改.eslintrc.cjs配置文件5.生成ESLint忽略文件6.在package.js…

K8S pod资源、探针

目录 一.pod资源限制 1.pod资源限制方式 2.pod资源限制指定时指定的参数 &#xff08;1&#xff09;request 资源 &#xff08;2&#xff09; limit 资源 &#xff08;3&#xff09;两种资源匹配方式 3.资源限制的示例 &#xff08;1&#xff09;官网示例 2&#xff0…

张勇时代落幕 蔡崇信能否让阿里变得更好

这两年&#xff0c;互联网行业似乎迎来了组织变革潮&#xff0c;只是谁也没想到&#xff0c;阿里的来得这么快&#xff0c;这么彻底。 9月10日晚&#xff0c;阿里巴巴董事会主席蔡崇信发布全员信&#xff0c;宣布已按计划完成集团管理职务交接&#xff0c;由他接任集团董事会主…