基数排序(图解, 生动形象的讲解,讲通原理和代码)

为什么学了快排等优秀的排序还要学习基数排序?因为基数排序有其他让人意象不到的快处,末尾介绍。

故事

"你们是我教过最差的一届, 这都要期末考试了, 还考成这样, 从今天起每天一场模拟考试, 越到后面考试越是重要, 大家知道吗?"班主任猛的一拍桌子,整个峡谷为之震动。于是每天, 学生们要进行苦逼的考试, 还要进行排名,你说苦不苦。而且,胆敢有不参加考试的同学,当天考试成绩一律按照零分进行排名。但是有谁知道呢, 险恶的班主任竟然用这些考试成绩最为最后排名的一些依据: 最后期末考试成绩相同的排名, 按照最近的一次考试排名, 如果仍然有相同, 再按这次考试最近一次考试的成绩排名, 依次类推, 用来保证排名的公正性 。
(老师其实就相当于要去完成了一次广义的基数排序啦。)

基数排序

具体到某个数字,每个数字影响力最大的其实就是最高位的数字,比如“520”中的"5"。但是由于位数不同,只比较很多数字的最高位是无法得出大小关系的, 比如"9"和"80",“9"的最高位是"9”,“80"的最高位是"8”,“9”>“8”,但是"9"<“80”,就像不可能用别人的期末考试成绩和自己的月考来比,这毫无可比性啊!于是我们可以通过补零来挽救这种情况,“9”写成“09”就可以直接按位来比较的。(怪不得老师要强制把没来人的那场考试当零分来计算)
位数相同了以后(既然同学们每场试都有排名了),我们就可以进行数字的排序了(同学们的排名就有了)。就是按最高位排序,如果相同的话就接着比较下一位,依次类推得到总的排名。(“期末考试成绩相同的话,就按最近的平时成绩来”,老师:“不愧是我”)

如何实现

令老师头痛的是怎么快速的进行这样的排序呢?毕竟我们老师做什么都要快。而作为班上的顶级快枪手的你也一样,于是你向老师喃喃的说道为什么不从“小”的开始呢?
“对啊,从第一次考试开始往后接着排序并一直维持上一次的基本顺序,这样递推到最后就可以了,可是,那具体如何实现呢?”
“哎,就想看h书一样,万般描述都是虚无的,咱们还是看图吧!”
现在我们要排序如下的数组, 然后如图是最基本的实现效果
在这里插入图片描述
具体到实践操作中:

  1. 首先构建是十个桶,就是十个容器, 用来存储数字,如图
    在这里插入图片描述2. 一开始,按位将数字放入桶中,每个桶中的数字的共同的数字特征是最低位相同(位次依次类推),比如“542”的最低位是2,就放在2号桶中,看图:

在这里插入图片描述

  1. 然后按顺序取出。判断是否已经取到最高位,如果已经取到最高位则退出
    在这里插入图片描述

  2. 此时再按下一位的数字,此时也就是十位来依次放入,如果有数字“2”不满十位, 就补零,理由如上面所讲,相当于回到步骤2: 在这里插入图片描述
    当步骤2条件满足就跳出, 这个时候就已经排序好了

代码实现

到了最重要的代码实现环节了,而在实际代码中, 桶中存储的其实是桶中每个数字的最高排名,拿出时则是已经排序好了的,具体实现见代码和详细注释。
注:以下代码只适用于正整数,对于负数则需要开二倍数组

int a[] = {1,6,4231,353,34,53};
    //v: 序列存储
    vector<int> v(a,a+sizeof(a)/sizeof(a[0]));
    //序列中最高位所在值
    int m = *max_element(v.begin(),v.end());
    //bucket: 桶  exp: 当前位数指示,比如去十位,则exp = 10
    int bucket[10],exp = 1;
    //res: 暂存排序好的序列
    int res[1000];
    while(exp<m){
        //初始化桶,清空桶内原有数字
        memset(bucket,0,sizeof(bucket));
        //按位入桶, 获取每个桶内有多少个满足条件的数字
        for(int i = 0;i<v.size();i++) bucket[v[i]/exp%10]++;
        //通过累加bucket内的值,从而得到各个桶内数字的在一轮结果中的下标
        for(int i = 1;i<10;i++) bucket[i] +=bucket[i-1];
        //逆序遍历,得到第一轮排序好的数组,逆序是因为这样可以不打乱原来的排序
        for(int i = v.size()-1;i>=0;i--) res[--bucket[v[i]/exp%10]] = v[i];
        //将第一轮排序好的数组赋值给原数组
        for(int i = 0;i<v.size();i++) v[i] = res[i];
        //转到下一位
        exp*=10;
    }

这只是正整数的排序, 其实基数排序不仅仅可以使用在这一个方面,还能做许多其他排序做不到的事情,比如数据结构进阶中的后缀数组。
既然会了基数排序, 还不赶紧去学一下后缀数组?
相信大家到这里已经学会了基数排序的运用, 如果有需要改正或者需要补充的地方欢迎评论区留言。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值