数据结构:分配排序
插入类、交换类、选择类和归并类排序方法,都是在比较关键字大小的基础上实现排序的,而分配类排序不需要比较关键字的大小。
分配排序,是指通过对待排序记录进行若干趟分配与收集实现排序,是一种借助多关键字排序思想对单关键字进行排序的方法。基数排序(Radix Sorting)是最典型的分配类排序。
一、多关键字排序思想
先看一个具体例子。已知一副扑克牌,52张牌面的次序关系为
1 |
|
即,每张牌有两个关键字:花色(♣<♦<♥<♠)和面值(2<3<…<A),且花色的地位高于面值。在比较任意两张牌面的大小时,必须先比较花色,若花色相同,则再比较面值。由此,将扑克牌整理成上述次序关系时,有以下两种排序法。
最高位优先法:先按不同花色分成有次序的4堆,每堆牌均具有相同的花色,然后分别对每堆牌按面值大小整理有序。
最低位优先法:先按不同面值分成13堆,然后将这13堆牌按照面值的次序收集到一起(将13堆牌自小至大叠在一起,3在2之上,4在3之上,…,最上面的是4张A);再重新对这些牌按不同花色分成4堆,最后将这4堆牌按花色的次序再收集到一起(♣在最下面,♠在最上面)。此时,同样可以得到一副,满足上述次序关系的牌。该方法是一种分配与收集交替进行的方法,其过程如下图所示。
二、链式基数排序
2.1、算法思想
基数排序的思想类似于上述最低位优先法的洗牌过程,是借助分配和收集操作,对单逻辑关键字进行排序的一种内部排序方法。
有的逻辑关键字可以看成是由若干个关键字复合而成的。例如,若关键字是数值,且其值都在范围内,则可把每位上的十进制数字看成一个关键字,即可认为K由3个关键字组成,其中是百位数,是十位数,是个位数;若关键字K是由5个字母组成的单词,则可看成是由5个关键字组成,其中是(自左至右的)第j个字母。这样分解而得的每个关键字都在相同的范围内(对数字来说,是;对于字母来说,是),故可以按照分配和收集的方法进行排序。
假设记录的逻辑关键字由d个关键字组成,每个关键字可能取rd个值。只要从最低数位关键字起,按关键字的不同值将序列中记录分配到rd个队列中后再收集之,如此重复d次完成排序。按这种方法实现排序称之为基数排序 ,其中“基”指的是rd的取值范围,在上述两种关键字的情况下,rd的取值范围分别为10和26。
2.1.1、示例
接下来,来看一个具体例子。
假设待排序记录的关键字都是数值,且值都在范围内。首先,以链表存储n个待排序记录,链表头指针指向第一个记录,如图下所示。然后,通过三趟分配和收集操作来完成排序。
第一趟分配对最低数位关键字(个位数)进行,改变记录的指针值,将链表中的记录分配到10个链表中,每个链表中的记录关键字的个位数相等,如下图(a)所示,其中f[i]和e[i]分别为第i个链表的头指针和尾指针;第一趟收集是改变所有非空链表的表尾记录的指针域,令其指向下一个非空链表的表头记录,重新将10个链表中的记录链成一个链表,如下图(b)所示。
第二趟分配和第二趟收集是对十位数进行的,其过程和个位数相同。分配和收集结果分别如下图(a)和下图(b)所示。
第三趟分配和第三趟收集是对百位数进行的,过程同上。分配和收集结果分别如下图(a)和下图(b)所示。至此排序完毕。
2.1.2、存储结构
基数排序在具体实现时,一般采用链式存储结构。这里,算法实现时采用静态链表(即用数组描述的链表),以便于更有效地存储和重排记录。相关数据类型的定义如下:
1 |
|
2.2、算法描述
1 |
|
2.3、算法分析
从时间上来看。对n个记录(假设每个记录含d个关键字,每个关键字的取值范围为rd个值)进行链式基数排序时,每一趟分配的时间复杂度为O(n),每一趟收集的时间复杂度为O(rd),整个排序需进行d趟分配和收集,所以时间复杂度为O(d (n +rd ))。
从空间上来看。所需辅助空间为2rd个队列指针,另外由于用链表做存储结构,则相对于其他以顺序结构存储记录的排序方法而言,还增加了n个指针域的空间,所以空间复杂度为O(n+rd )。
2.4、算法特点
基数排序算法的特点为:
- 稳定排序。
- 可用于链式结构,也可用于顺序结构。
- 时间复杂度可以突破基于关键字比较类方法的下界,达到O(n)。
- 基数排序使用条件有严格的要求:需要知道各级关键字的主次关系和各级关键字的取值范围。