如果我们有M个数,其范围是1~M,我们可以利用其范围的有限性结合数组实现O(m)的一种排序算法。
我们定义一个数组Count,大小为M,初始化为0,于是Count有M个桶,开始都是空的,然后根据输入的内容,刚在相应的数组上加1,
当一躺遍历完后,则按照数组下标的顺序输出即可。
这种排序就是桶排序(bucket sort ),适用于范围有限的数或字符。
而基数排序(radix sort)则是根据桶排序推广而来,人为的划分不同的关键字,对不同的关键字进行桶排序,最后获得有序结果。
对于多关键字排序,我们可以先对主位关键字进行排序,将序列分成若干子序列,然后对子序列进行排序,这种方法称为最高位优先(Most Significant Digit first)MSD法,相反,先按最次关键字进行排序,然后对高一位的关键字进行排序,这种方法称为最低位优先(Least Significant Digit first)LSD。
感觉两者原理相同,但是在实现的时候有很大的区别:
如扑克牌排序,可以先按花色排好,再按面值排好,也可以先按面值排好,再按花色排。若先按花色,即MSD法,是先分桶,每桶之间的顺序先出来,再来对桶内按次关键字分桶,这里要换桶。若先按面值,即LSD,则是桶之间的顺序是仍不清楚的,但是能找出最小的几个有顺序的(桶中元素个数为1),然后按大一级的排又找出几个有顺序的,这是逐级有序的。
再举个例子,比如自然数排序,0,14,8,125,64。先按LSD排,按个位排是,0,14-64,125,8,则0和8的顺序已经出来了,最小两位,然后第二躺按十位0-8,14,125,64则14和65顺序出来了排后两位,最后一躺0-8-14-64,125。排序结束。不用保存中间结果,重新划分桶,比较高效。若按MSD,先按百位,125,0-14-8-64,知道125最大,但第二个桶内的顺序是完全混乱的,踢除125,重新分桶,算法上实现较麻烦。
要使用基数排序,我们首先就是要构造出不同级别的关键字,选择使用MSD法还是LSD法,存储结构是使用数组还是链表。
这里补充一个数据结构:链表的游标(cursor)的实现。
我们知道并不是所有语言都支持指针的,而且使用指针在很多情况下比数组麻烦的多。所以我们考虑数组来模拟链存储。
链表实现有两个重要的特点:
1、数据存储在一组结构体中,每一个结构体包含有数据以及指向下一个结构体的指针。
2、一个新的结构体可以通过调用malloc而从系统全局内存得到,并可通过调用free来释放
------摘自《数据结构与算法分析》
我们要用游标来模拟这两种特性。
首选定义结构体:
struct Node{
ElementType element;存储的元素
int next; //下一个元素的下标
}
初始化数组,结构如图所示:index表示数组下标
每个元素保存下一个元素的索引,一个接一个,但是数组第一位我们通常不用,因为我们要建立空位置的索引,而数组首部做为未使用位置的头结点。
当我们插入第一个元素a时,首先寻找空位置,从data[0].next处即为空,此时为指向1,则将a插入data[1]中,修改data[0]的next为2;
当然数组有上限,所以最后一个元素的next指向0,表示最后一个元素,若空位置索引指向0,表示已满。
最后可形成如下图所示表:两条链
空位置,0-3-5-7-0
已存储位置:1-2-4-6-0(1处要特殊标记为head,我想是可以放在0的element里)