计数排序与基数排序

计数排序与基数排序

计数排序

计数排序:使用一个数组记录序列中每一个数字出现的次数,将该数组的下标作为实际数据,元素的值作为数据出现的次数。例如对于序列[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+1for (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.mzph.cn/news/81897.shtml

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

相关文章

Redis模块二:缓存分类 + Redis模块三:常见缓存(应用)

缓存大致可以分为两大类&#xff1a;1&#xff09;本地缓存 2&#xff09;分布式缓存 目录 本地缓存 分布式缓存 常见缓存的使用 本地缓存&#xff1a;Spring Cache 分布式缓存&#xff1a;Redis 本地缓存 本地缓存也叫单机缓存&#xff0c;也就是说可以应⽤在单机环…

ffmpeg编译 Error: operand type mismatch for `shr‘

错误如下&#xff1a; D:\msys2\tmp\ccUxvBjQ.s: Assembler messages: D:\msys2\tmp\ccUxvBjQ.s:345: Error: operand type mismatch for shr D:\msys2\tmp\ccUxvBjQ.s:410: Error: operand type mismatch for shr D:\msys2\tmp\ccUxvBjQ.s:470: Error: operand type mismatch…

[JAVAee]Spring项目的创建与基本使用

目录 Spring项目的创建 Spring中Bean对象的存储与获取 存储Bean对象 获取并使用Bean对象 getBean方法的重载 本文章介绍了Spring项目创建与使用的过程与一定的注意事项. Spring项目的创建 首先在IDEA中,新建一个Maven 第二步,在pom.xml中写入spring的依赖. pom.xml是mav…

情侣飞行棋 情侣小游戏 2023 抖音

飞行棋网站地址:https://effect.guoyaxue.top/fxq/index.html#/ 以及各种新版来袭&#xff1a; 以及各种情侣小游戏合集 https://fxnew.guoyaxue.top/#/

【微信小程序】swiper的使用

1.swiper的基本使用 <jxz-header></jxz-header> <view class"banner"><swiperprevious-margin"30rpx"autoplayinterval"2000"indicator-dotsindicator-color"rgba(0,0,0,0.3)"indicator-active-color"#bda…

一百七十九、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;二&…

uniapp项目实践总结(十七)实现滚动触底加载

导语&#xff1a;在日测的开发过程中&#xff0c;经常会碰到页面需要渲染大量数据的情况&#xff0c;这时候就需要用到滚动加载功能&#xff0c;下面总结一下方法。 目录 原理分析实战演练案例展示 原理分析 使用scrolltolower事件来监听滚动到底部&#xff0c;然后加载下一…

idea之maven的安装与配置

我们到maven的官网里下载maven&#xff0c;地址&#xff1a;https://maven.apache.org/download.cgi下载完成后解压即可配置环境变量 此电脑–>右键–>属性–>高级系统设置–>环境变量–>系统变量&#xff08;S&#xff09;–>新建一个系统变量 变量名&…

短效代理IP与长效代理IP:应用场景与选择方法

随着互联网的迅速发展&#xff0c;越来越多的网站开始采取反爬虫措施&#xff0c;以保护自己的数据安全。而作为一种常见的反爬虫工具&#xff0c;代理IP也被广泛应用。代理IP分为短效代理IP和长效代理IP两种类型&#xff0c;本文将介绍它们的区别和应用场景。 一、短效代理IP …

数据结构-----队列

目录 前言 队列 定义 队列的定义和操作方法 队列节点的定义 操作方式 顺序表实现队列&#xff08;C/C代码&#xff09; 链表实现队列&#xff08;C/C代码&#xff09; Python语言实现队列 前言 排队是我们日常生活中必不可少的一件事&#xff0c;去饭堂打饭的时候排队&a…

彩色相机工作原理——bayer格式理解

早期&#xff0c;图像传感器只能记录光的强弱&#xff0c;无法记录光的颜色&#xff0c;所以只能拍摄黑白照片。 1974年,拜尔提出了bayer阵列&#xff0c;发明了bayer格式图片。不同于高成本的三个图像传感器方案&#xff0c;拜尔提出只用一个图像传感器&#xff0c;在其前面放…

tensorflow基础

windows安装tensorflow anaconda或者pip安装tensorflow&#xff0c;tensorflow只支持win7 64系统&#xff0c;本人使用tensorflow1.5版本&#xff08;pip install tensorflow1.5&#xff09; tensorboard tensorboard只支持chrome浏览器&#xff0c;而且加载过程中可能有一段…

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;我们不得不采用一些算法去做分词。 常用的分词工具 # encodingutf-8 import …

Python爬虫:获取必应图片的下载链接

文章目录 1. 前言2. 实现思路3. 运行结果 1. 前言 首先&#xff0c;说明一下&#xff0c;本篇博客内容可能涉及到版权问题&#xff0c;为此&#xff0c;小编只说明一下实现思路&#xff0c;至于全部参考代码&#xff0c;小编不粘贴出来。不过&#xff0c;小编会说明详细一些&a…

three.js——模型对象的使用材质和方法

模型对象的使用材质和方法 前言效果图1、旋转、缩放、平移&#xff0c;居中的使用1.1 旋转rotation&#xff08;.rotateX()、.rotateY()、.rotateZ()&#xff09;1.2缩放.scale()1.3平移.translate()1.4居中.center() 2、材质属性.wireframe 前言 BufferGeometry通过.scale()、…

LeetCode: 4. Median of Two Sorted Arrays

LeetCode - The Worlds Leading Online Programming Learning Platform 题目大意 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。 请你找出这两个有序数组的中位数&#xff0c;并且要求算法的时间复杂度为 O(log(m n))。 你可以假设 nums1 和 nums2 不会同时为空。 …

css动画旋转分层旋转图

1.代码 <template><view class"animations"><view class"animation animation1"><view class"animate1"></view><view class"animate2"></view><view class"animate3">&l…

Qt --- Day02

实现效果&#xff1a; 点击登录&#xff0c;检验用户密码是否正确&#xff0c;正确则弹出消息框&#xff0c;点击ok转到另一个页面 不正确跳出错误消息框&#xff0c;默认选线为Cancel&#xff0c;点击Yes继续登录 点击Cancel跳出问题消息框&#xff0c;默认选项No&#xff0c…

Java文字描边效果实现

效果&#xff1a; FontUtil工具类的完整代码如下&#xff1a; 其中实现描边效果的函数为&#xff1a;generateAdaptiveStrokeFontImage() package com.ncarzone.data.contentcenter.biz.img.util;import org.springframework.core.io.ClassPathResource; import org.springfr…