news 2026/5/8 6:12:22

排序--基数排序

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
排序--基数排序

一、不基于比较的排序算法

1.1、计数排序

这是一种另类排序,它不是基于比较的排序算法。比较小众,根据数据的分布情况,即频率。

1.2、基数排序

数据结构不统一,一般采用队列,先进先出。

比如[13,17,26,72,100],先找最高位有几位,有3位,进行补齐
|
|

[013,017,026,072,100],然后对每位准备一个队列,从个位开始存放
|
|

0位队列存放:100 2位队列存放:072 3位队列存放:013 6位队列存放:026 7位队列存放:017
然后依次从左到右取出数据
|
|

[100,072,013,026,017],然后准备十位队列,同理
|
|

0位队列存放:100 1位队列存放:013 017 2位队列存放:026 7位队列存放:072
然后依次从左到右取出数据
|
|

[100,013,017,026,072],同理百位
|
|

0位队列存放:013,017,026,072 1位队列存放:100
然后依次从左到右取出数据
[013,017,026,072,100]有序

理由:

  1. 基数排序是基于计数排序的扩展,是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。
    要求:

    要排序的东西必须要有进制,比如十进制、二进制等。

    核心点:

    按位分配收集
// 大致情况如下:voidradixSort(vector<int>&nums){if(nums.empty()||nums.size()<2)return;radixSort(nums,0,nums.size()-1,maxBits(nums));}staticintmaxBits(constvector<int>&nums){if(nums.empty())return0;intmaxVal=INT_MIN;for(intnum:nums){maxVal=max(num,maxVal);}// 处理最大值为0的情况if(maxVal==0)return1;intres=0;// 处理负数:取绝对值inttemp=abs(maxVal);while(temp!=0){res++;temp/=10;}returnres;}

基数排序的实现

// digit: 最大位数staticvoidradixSortImpl(vector<int>&nums,intbegin,intend,intdigit){constintradix=10;// 基数,这里是十进制inti=0,j=0;// 辅助数据,用于存储排序结果vector<int>bucket(end-begin+1);// 对每一位进行排序for(intd=1;d<=digit;++d){// 计数数组,记录每个数字出现的次数vector<int>count(radix,0);// 统计当前位上每个数字出现的次数for(i=begin;i<=end;++i){j=getDigit(nums[i],d);count[j]++;}// 将计数转换为前缀和,表示每个数字在输出数组中的最后位置// 注意:这里从 i=1 开始累加,count[0] 保持不变for(i=1;i<radix;++i){count[i]+=count[i-1];}// 从后向前遍历,保持稳定性for(i=end;i>=begin;--i){j=getDigit(nums[i],d);// count[j] - 1 是当前数字应该放入的位置bucket[count[j]-1]=nums[i];count[j]--;}// 将桶中的数据复制回原数组for(i=begin,j=0;i<=end;++i,++j){nums[i]=bucket[j];}}}

获取数字x的第d位数(从个位开始,d=1表示个位)

staticintgetDigit(intx,intd){// 处理负数:先取绝对值intnum=abs(x);// 计算第d位的数字return(num/static_cast<int>(pow(10,d-1)))%10;}// 基数排序的入口函数voidradixSort(vector<int>&nums){if(nums.empty()||nums.size()<2)return;// 获取最大位数intdigit=maxBits(nums);// 调用实际的排序函数radixSortImpl(nums,0,nums.size()-1,digit);// 处理负数:将负数放到数组前面// 基数排序通常不能直接处理负数,需要特殊处理// 先处理正数部分,再处理负数部分}

处理负数的基数排序版本

voidradixSortWithNegative(vector<int>&nums){if(nums.empty()||nums.size()<2)return;// 分离正数和负数vector<int>negatives,positives;for(intnum:nums){if(num<0){negatives.push_back(-num);// 转换为正数处理}else{positives.push_back(num);}}// 分别排序radixSort(positives);radixSort(negatives);// 合并结果:先放负数(从大到小),再放正数intindex=0;// 负数要恢复为负数,并且因为我们对绝对值排序了,所以需要反转for(inti=negatives.size()-1;i>=0;--i){nums[index++]=-negatives[i];}// 正数直接放入for(intnum:positives){nums[index++]=num;}}

二、实操演练

2.1、以LeetCode 164为例

题目描述:

给定一个无序的数组 nums,返回 数组在排序之后,相邻元素之间最大的差值 。如果数组元素个数小于 2,则返回 0 。

您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。

示例1:

输入: nums = [3,6,9,1]

输出: 3

解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3

解题思路:

  1. 题目要求时间复杂度在线性时间内,就等同于O(n),所以不能使用O(nlogn)的排序算法,比如快速排序、归并排序、堆排序等。
  2. 那么基数排序刚好满足时间复杂度O(n)
  3. 可以先排序数组,然后遍历排序后的数组,计算相邻差值并找出最大值。
classSolution{public:intmaximumGap(vector<int>&nums){if(nums.size()<2)return0;intret=0;radixSort(nums);for(inti=0,j=1;j<nums.size();i++,j++){ret=std::max(ret,abs(nums[i]-nums[j]));}returnret;}staticintmaxBits(constvector<int>&nums){if(nums.empty())return0;intmaxVal=INT_MIN;for(intnum:nums){maxVal=max(num,maxVal);}// 处理最大值为0的情况if(maxVal==0)return1;intres=0;// 处理负数:取绝对值inttemp=abs(maxVal);while(temp!=0){res++;temp/=10;}returnres;}staticvoidradixSortImpl(vector<int>&nums,intbegin,intend,intdigit){constintradix=10;// 基数,这里是十进制inti=0,j=0;// 辅助数据,用于存储排序结果vector<int>bucket(end-begin+1);// 对每一位进行排序for(intd=1;d<=digit;++d){// 计数数组,记录每个数字出现的次数vector<int>count(radix,0);// 统计当前位上每个数字出现的次数for(i=begin;i<=end;++i){j=getDigit(nums[i],d);count[j]++;}// 将计数转换为前缀和,表示每个数字在输出数组中的最后位置// 注意:这里从 i=1 开始累加,count[0] 保持不变for(i=1;i<radix;++i){count[i]+=count[i-1];}// 从后向前遍历,保持稳定性for(i=end;i>=begin;--i){j=getDigit(nums[i],d);// count[j] - 1 是当前数字应该放入的位置bucket[count[j]-1]=nums[i];count[j]--;}// 将桶中的数据复制回原数组for(i=begin,j=0;i<=end;++i,++j){nums[i]=bucket[j];}}}// 获取数字x的第d位数(从个位开始,d=1表示个位)staticintgetDigit(intx,intd){// 处理负数:先取绝对值intnum=abs(x);// 计算第d位的数字return(num/static_cast<int>(pow(10,d-1)))%10;}// 基数排序的入口函数voidradixSort(vector<int>&nums){if(nums.empty()||nums.size()<2)return;// 获取最大位数intdigit=maxBits(nums);// 调用实际的排序函数radixSortImpl(nums,0,nums.size()-1,digit);}};

虽然能pass了,但基本思想还是要将数组进行完全排序之后,再重新计算一遍相邻差值,为了找最大间隔而完成了全部排序;其实,我们只需要找到最大间隔,不需要完全排序,所以可以优化一下,只对最大间隔的元素进行排序,这样时间复杂度会变得更低,虽然都是O(n)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 12:01:01

揭秘大模型深度研究:从多智能体协作到结构化报告生成的全流程

深度研究(Deep Research)已成为现代大模型平台的标准能力&#xff0c;通过多智能体协作完成长时间研究任务。文章解析了其高层架构(编排者、子代理、综合与引用代理)&#xff0c;对比了OpenAI、Gemini、Claude等平台的实现差异&#xff0c;详细阐述了从用户查询、初始规划、并行…

作者头像 李华
网站建设 2026/5/3 9:27:08

2026版大模型应用开发全攻略:零基础入门到精通,一篇文章搞定,非常详细收藏这一篇就够!

“ 大语言模型应用开发流程包括筛选应用场景、企业知识管理、训练场景大模型、业务系统融合、大模型安全体系建设、持续改进体验等多个环节。通过将AI智能体集成到数字化系统中&#xff0c;将业务数字化系统升级为智能化系统&#xff0c;从而实现人类员工与数字员工的高效协作。…

作者头像 李华
网站建设 2026/5/3 9:27:06

经典算法题型之俄罗斯套娃信封问题(一)

我们先来看题目描述:给定一些标记了宽度和高度的信封&#xff0c;宽度和高度以整数对形式 (w, h) 出现。当另一个信封的宽度和高度都比这个信封大的时候&#xff0c;这个信封就可以放进另一个信封里&#xff0c;如同俄罗斯套娃一样。请计算最多能有多少个信封能组成一组“俄罗斯…

作者头像 李华
网站建设 2026/5/2 21:54:14

AN-93双麦降噪远场拾音模块技术解析:从算法到落地的全维度突破

AN-93双麦模组规格书点击查看 在语音交互技术全面渗透的当下&#xff0c;远场拾音与噪声抑制能力已成为衡量音频设备性能的核心指标。单麦方案因无法区分空间声源信息&#xff0c;难以应对复杂噪声环境&#xff1b;多麦方案则普遍面临成本高昂、体积偏大、集成难度较高的痛点。…

作者头像 李华