Radix-Sorting(线性阶 多关键字排序算法 基数排序)

来源:互联网 发布:php soapclient xml 编辑:程序博客网 时间:2024/06/05 16:14

1.引入:

我们发现也经过证实,交换式的排序的算法的时间复杂度的下界是O(n*lgn),但是这真的就是我们的排序算法的极限了吗,事实并不是这样的,我们的多关键字的分配式排序--本文的基数排序就是如此却是打破了这样的瓶颈
分配式的Radix-Sorting算法成功的将我们的算法的复杂度优化到无限接近线性阶的完美的O(n),下面,我们就来一一道来

2.前身:

桶式排序:
我们先假定排序这样的一个数组
1,9,4,8,2,7,5,6,3,10
很显然,你们都会说出什么快排啊,归并排序,插入排序啊,堆排序啊什么的,但是我们就题目本身来看的话,可不可以这么来考虑
我们现在假设这里有10个桶,每个桶用来存放权值是相应的标号的数字的个数
那么很显然初始的状态应该是
0 0 0 0 0 0 0 0 0 0
按照题目来看的话,我们扫描一遍,将相应的数组入桶,结果为
1 1 1 1 1 1 1 1 1 1
最后我们从桶1开始一直输出到桶10
很明显就会得到
1 2 3 4 5 6 7 8 9 10
很显然,这样的算法我们的时间耗费完全只是在扫描和输出上了,完全接近我们的O(n)的线性阶时间复杂度

但是我们需要明确一点,这里的桶式排序有一些明显的缺陷
1.无法排序负数
2.需要消耗大量的额外空间,对于极限情况1,2000来说,显然会造成力不从心的情况

于是,这里就引出了我们的基数排序

3.基数排序概念

基数排序的专业说法应该是:对单逻辑关键字的多关键字排序,也就是说,我们将待排对象分类城多种关键字,我们对不同的关键字都进行一趟桶式排序,然后我们对结果进行收集(这里的描述有一些偏向LSD,但是会方便我们的理解,这里我们不要过深究就这一点)
定义:
对有n个关键字的记录序列
{r1,r2,r3,r4,r5...rn}
每个记录中都含有d个关键字{k0,k1,k2,k3,k4,...kd}
我们的基数排序要求对于任意的两个r之间我们都要满足前者的k关键字必须全部大于或者小于后者的全部关键字

4.思路分类:

对于基数排序,我们这里存在着两种不同的排序思路

1.LSD(最低位优先)

这里的最低位优先的意思是,我们从最次为的关键字进行开始,每一次进行一趟基数排序然后收集,当所有的关键字全部都匹配完全之后,我们得到的就是有序的序列
这里的限制的终止情况是,我们必须要对所有的关键字全部都进行匹配,但是一旦在出现数字(待排对象)之间的位数差距比较大的情况,我们有可能做大量的无用功

2.MSD(最高位优先)

这里的MSD法参透了一些递归的思想,在这里,我们首先从高位开始匹配,每一次入桶之后,我们对每一个桶的序列也分别进行一次MSD递归调用,直到最后最后我们发现每个子桶中的元素的个数都只是<=1的情况的话,很明显,这时候我们就该强行终止了递归调用,返回我们的有序序列
MSD对于上述的位数差距比较大的情况来看的话,可以大幅度减少我们的排序次数

5.复杂度的优化想法思路

1.参照网上的大神的描述,在整数排序的额时候,我们可以将基数设置的大一些
2.在对证书进行基数排序的时候,那面会出现pow函数的调用,这时候我们的时间耗费就会出现一些提升,在这里,我们的优化思路是采用位运算的想法,将基数设定成2的幂,那么我们在进行反馈映射的过程的时候,利用位运算可以大幅度降低我们pow函数和我们的触发造成的恐怖的时间耗费,毕竟计算机还是一个二进制机器
3.如果我们只是单纯地对数组进行基数排序的话
我们的时间耗费大致是O(d*(3*n+radix)),空间耗费是n+radix
但是如果我们在这里利用链表的话
时间复杂度大致是O(d*(n+radix)),空间耗费是radix
为什么呢
ps:以下的思路是LSD

1.数组法

我们的工具:bucket桶数组,count记录每个桶的数目
我们先来看简单的数组的操作过程
1.初始化count
2.扫描一遍数组O(n),将count更新维护
5.扫描一遍count,确定每个桶的右边界,方便我们的插入操作
4.扫描一遍数组O(n),建立映射,数组元素入桶
5.将bucket中的元素返还(倒进)原数组中,O(n)
以上是一趟桶式排序,对于我们的数字的范围d(如果用十进制的例子来看的话,那就是d是位数)

2.链式基数排序(在这里,我们用链表还是静态量表的实现原理都是一样的,我们这里随便举个例子,用静态链表来描述好了)

我们的工具radix个<head,tail>链队列的头尾指针O(2*radix)的空间复杂度
1.扫描一遍,根据映射我们将链的节点插入相应的链队列O(n)
2.遍历所有的radix链队列,将链队列的头尾指针相互串接,构成新的链O(radix)
3.重复d次
时间复杂度是O(d*(n+radix)),空间复杂度是O(2*radix+n),n是链节点的指针域

通过上述比较,我们来看一下

 空间复杂度时间复杂度数组法O(n+ra)O(d*(3*n+ra))链表法O(n+2*ra)O(d*(n+ra))显然从上述表格来看,我们的链式的结构更具有优越性
当然,我所有的情况都会用代码示例一遍

6.优缺点限制以及特殊情况的处理

优点:排序稳定性:
无论是从上面的链式还是数组的方式,我们都可以清楚地的看到,通过特殊的处理(链队列,数组count的利用)都是的我们的基数排序是稳定的

1.从上面的情况,我们已经看到了,桶式排序不能处理负数,不能处理浮点数,也不能处理字符串
但是对于基数排序,并不是这样的
负数的话,我们可以将负数和整数进行分类考虑最后将结果合并就好
浮点数的话,我们可以将浮点数承上一个较大的整数将其装花城整数来进行操作
字符串的话,我们将字符串的每个字符都当成是一个关键字,从而更加方便的实现基数排序

优点:基数排序不是交换式的,而是分配式的排序,所以说,我们的时间复杂度可以轻而易举的达到线性阶

缺点:基数排序的内存占用和归并排序一样,非常的大,在我们的内存吃紧的情况下,基数排序不要进行考虑,但是对于我们的内存宽松,时间要求搞笑的时候,基数排序旺旺可以达到比快排更好的效果
基数排序的LSD进行之前,我们必须要了解到所有的数组的最大的上限

7.相应的核心代码段

附上我的CSDN snippts连接:
基数排序相关代码段

0 0
原创粉丝点击