本文内容为:多关键字排序的思想、基数排序主流方法:最低位优先法、基数排序的时间、空间复杂度、稳定性分析和基数排序适用场景。最后还有单逻辑关键字、计数排序和桶排序的简单介绍。
基数排序很特别,它不直接比较关键字的大小进行排序,而且比较关键字各个元的大小进行排序。基数排序是一种借助多关键字排序的思想对单逻辑关键字(各个位逻辑相同,即基数相同)进行排序的方法。
关键字通常是多元的,比如十进制数684就有三个元,百位、十位、个位。其中百位就是权重最大的最主位关键字,个位是权重最小的最次位关键字。其中每一位有多少种可能称为基数r,如十进制数基数为10,二进制数基数为2。
实现多关键字排序有两个方法:假设共有d个元(,,...,)
- 最高位优先(MSD)法:先根据权重更大的位 去给我们建立的基数个子序列排序,将得到的每个序列按进行排序又各自划分成基数个子序列,按此操作,最后将所有子序列依次连接成一个有序序列。
- 最低位优先(LSD)法:先根据权重最小的位去排得到一个序列,然后按更大权重的位去排这个序列,按此操作,最后得到一个有序序列。
基数排序是对单逻辑关键字(各个位逻辑相同,即基数相同)进行排序的,那么这时用LSD法就更为方便,只需从最低位起,把关键字分配到r个队列中然后收集之,如此重复d次。下面四个图描述了这个过程。
- 分配:开始时,把r个队列全部置成空队列,然后依次考察线性表中的每个结点,若结点该位关键字为k,则放到队列中。
- 收集:把这个r个队列中的结点依次首尾相接,得到新的结点序列,从而组成新的线性表。
- 空间复杂度:前面已经说了基数排序通常是基于链式存储实现的,我们设置的链队列只包含了一个队头指针和一个队尾指针,队列的结点是我们待排序列中的结点,所以队列不额外开辟结点空间。基数排序的空间复杂度为O(r),注意此处问题规模是r,并不是结点个数n。
- 时间复杂度:一趟分配是O(n),一趟收集是O(r),共计d趟分配、收集,所以基数排序的时间复杂度为O(d(r+n))。注:n为关键字个数,d为把关键字分为了多少个元,r为每个元有多少种取值
- 稳定性分析:两个相同的关键字,排在前面的分配时先进队列,收集时先出队列,顺序没有改变,所以基数排序是稳定排序。基数排序的每一趟必须都是稳定的,因为我们每一趟比较的都是某一个位的大小,对于这一位相等,它的上一位不一定相等,不能被打乱。比如有{25,32,34,},排个位之后为{32,34,25},排十位用不稳定就会为{25,34,32}。
基数排序擅长解决的问题:
- 数据元素个数n很大。
- 数据元素的关键字有为d位分主次的元,且d较小。
- 每位元的取值范围不大,即基数r较小
计数排序:计数待排序列中每个元素出现的次数,并把每个元素的数量都记录下来。比如待排序列为{5,4,3,2,2},我们创造一个长度为5-2+1的数组(即最大值减最小值加1,保证每个元素都有地方去计数),然后我们遍历待排序列后数组中就有二个2,一个3,一个4,一个5这样的信息了,我们就输出2,2,3,4,5这个排好的序列了。该过程也不是基于比较的排序算法,以空间换时间,时间复杂度为O(n)。我们可以看出基数排序每一趟其实类似一次计数排序,只不过统计的不是元素的数量而是元素本身。
桶排序:将数组分到有限数量的桶子里,每个桶子再分别排序(有可能是使用别的排序算法或者继续使用桶排序递归地进行排序)。该过程对输入的数据作了某些假定,所以时间复杂度比基于比较的排序算法时间复杂度低,属于是空间换时间了。很显然基数排序(MSD)就是桶子里继续使用桶排序递归地进行排序。而基数排序(LSD)是先分到桶子里,但是桶子内部不排序就收集出来,再分配再收集直到完成排序。
总结:基数排序、桶排序、计数排序都不是基于比较的排序算法,它们都以空间消耗提升换取了时间消耗减少。它们都是在某方面作了假定再进行排序,所以个人认为抽象一下其思想都是类似的。
单逻辑关键字的理解:单逻辑关键字每一位都是相同的逻辑,比如我们基数排序时要排的整数的每一位都是十进制的逻辑。这样我们进行基数排序时可以设置基数r个反复使用的队列。如果不是单逻辑而是每一位都有它自己的逻辑,比如52张牌,面值位基数为12,花色位基数为4。那么就演变成了多关键字排序。这就和“基数排序是一种借助多关键字排序的思想对单关键字进行排序的方法”这句官方概述相同。