链式基数排序空间复杂度_算法学习笔记(22): 基数排序

1219651faa435d755261be39e0ef0121.png

任何计算机相关专业的学生都学过很多排序算法,然而在算法竞赛中,我们会发现大部分排序算法都不怎么用得上(尤其是那堆

equation?tex=O%28n%5E2%29 算法),
快读排序归并排序已经基本够用了,它们的平均时间复杂度都是
equation?tex=O%28n%5Clog+n%29 。实际上,学术上已经有证明,任何
基于比较的排序算法的时间复杂度不能比这更优了。也就是说,如果不对待排序数据的性质有特殊要求,只任意给出偏序关系,那不可能存在小于
equation?tex=O%28n%5Clog+n%29 的排序算法。

但在竞赛中,这样的复杂度有时是不够的。而我们要排序的数据,有时恰恰满足一些特殊性质。例如当待排序数据是不太大的自然数(均满足

equation?tex=%5Cle+m )时,可以使用
计数排序,把待排序数据当作数组下标处理,时间复杂度是
equation?tex=O%28n%2Bm%29 ,并额外有
equation?tex=O%28m%29 的空间复杂度。
// 计数排序
int Cnt[MAXM];
void counting_sort(unsigned A[], int len)
{
    for (int i = 0; i < len; ++i)
        Cnt[A[i]]++;
    int pos = 0;
    for (int i = 0; pos < len; ++i)
        if (Cnt[i])
            for (int j = 0; j < Cnt[i]; ++j)
                A[pos++] = i;
}

对计数排序来说,当

equation?tex=m 稍微大一些,别说时间复杂度了,空间复杂度都会先告急。于是我们有对计数排序的优化:
基数排序(Radix Sort)。

基数排序也是针对自然数(其他数可以考虑转化为自然数)的排序,它的思想是,对数字的每一位分别进行计数排序。所谓的基数(radix),也就是“进制”的意思,所谓的“每一位”是该进制下的位,现在我们以10为基数作为例子:

fe461bfc2ccf9003691f48b883412a39.gif
第一趟,以最低位排序。

第一趟计数排序结束后,所有数已按最后一位排好序。

442540cc5a4ebc61f8caca68c9e8ad61.gif
第二趟,以次低位排序。

第二趟结束后,所有数末两位已排序。

……

做gif太累了,就不把整个过程做完了,现在我们已经可以观察到基数排序是怎样运作的了:从低位到高位,依次进行计数排序。当然并非必须用计数排序,只要保证是稳定的排序即可,但用计数排序的时间复杂度最好。至于基数排序的正确性,可以归纳地证明。

以65536为基数的基数排序代码如下:

inline void radix_sort(unsigned A[], int len)
{
    unsigned *B = new unsigned[len];
    int r = 65535, L[r + 1] = {0}, H[r + 1] = {0};
    for (int i = 0; i < len; ++i)
        L[A[i] & r]++, H[(A[i] >> 16) & r]++; // 计数
    for (int i = 1; i <= r; ++i)
        L[i] += L[i - 1], H[i] += H[i - 1]; // 求前缀和
    for (int i = len - 1; i >= 0; --i)
        B[--L[A[i] & r]] = A[i]; // 对低位进行计数排序
    for (int i = len - 1; i >= 0; --i)
        A[--H[(B[i] >> 16) & r]] = B[i]; // 对高位进行计数排序
    delete[] B;
}

这里解释一下为什么要求前缀和,例如数列3,1,4,3进行计数后得到的数组L应该是:

31b8dcdaa34458472b677048136a46dd.png

那么求前缀和后,L数组就变为:

efdda78f80e704b6c25c65dc5232f6c8.png

这时每个(原来存在的)数i对应的L[i],就是排序后最后一个i的位置加上1。


一般而言,对于32位整数而言,选择65536作为基数是很合理的(快且好写,时间复杂度接近 )。但洛谷P4604 挑战这道(毒瘤)题为了利用硬件原理卡常数要把基数设为256,以256位基数的代码可以参见我学长的一个回答。

自己想了个新的排序算法,想写成论文,可以发哪些期刊呢?​www.zhihu.com
zhihu-card-default.svg

在他的回答中,还给出了用基数排序排浮点数的一个方法。但其实排序浮点数的需求是相对较少的,倒是时不时需要排序有符号数,这时我们只需要先把所有数加上0x80000000,排序结束后再减去即可。


Pecco:算法学习笔记(目录)​zhuanlan.zhihu.com
zhihu-card-default.svg
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值