作为数据结构的课程笔记,以便查阅。如有出错的地方,还请多多指正!
注:C++忘得太厉害了。。算法先用C实现,等之后复习了再改成C++
MSD——必须将序列逐层分割成若干子序列,再对各子序列分别排序
LSD——不必逐层分割成子序列,并且可不通过关键字比较,而通过若干次分配与收集实现排序
对于多关键字的记录序列,通常利用LSD法借助“分配-收集”进行排序
对于数字型或字符型的单逻辑关键字,可以看成是由多个数位或多个字符构成的多关键字(例如三位数的排序可以看作有3个关键字:百位、十位、个位),也可以采用“分配-收集”的排序方法
链式基数排序:用静态链表作存储结构的基数排序
f[i]
和e[i]
分别为第i
个队列的头指针和尾指针next
。next
中存的是链表中下一个结点在数组中的索引。第0个结点为头结点,不存数据,它指示了第一个结点的位置。数据域为1个数组,在里面存各个关键字(十进制数的不同位),默认0号关键字为最次要关键字(个位)#define MAX_SIZE 100 //待排元素个数的最大值
#define MAX_KEY_NUM 8 //最多的关键字个数 这里默认最大为8位数
//静态链表结点
typedef struct {
int keys[MAX_KEY_NUM];
int next;
}SLCell_t;
//静态链表
typedef struct {
SLCell_t data[MAX_SIZE+1];
int keyNum; //关键字个数
int len; //静态链表长度
}SLList_t;
int
数组表示。指针域中存的值也为数据在静态链表的数组中的索引#define RADIX 10 //关键字基数 这里排序的是10进制数,因此基数为10
typedef int PtrArr_t[RADIX]; //指针数组类型
//按照第 key_index 个关键字进行一趟分配
void Distribute(SLList_t* sllist, int key_index, PtrArr_t* f, PtrArr_t* e)
{
//清空队首指针
for (int i = 0; i < RADIX; ++i)
{
(*f)[i] = 0;
}
for (int p = sllist->data[0].next; p; p = sllist->data[p].next)
{
int key = sllist->data[p].keys[key_index];
if (!(*f)[key])
{
(*f)[key] = p;
}
else {
sllist->data[(*e)[key]].next = p;
}
(*e)[key] = p;
}
}
void Collect(SLList_t* sllist, PtrArr_t* f, PtrArr_t* e)
{
int i;
//找到第一个非空队列
for (i = 0; (*f)[i] == 0; ++i)
{
}
sllist->data[0].next = (*f)[i];
for (int j = i + 1; j < RADIX; ++j)
{
if ((*f)[j])
{
sllist->data[(*e)[i]].next = (*f)[j];
i = j;
}
}
sllist->data[(*e)[i]].next = 0;
}
void SLList_radix_sort(SLList_t* sllist)
{
PtrArr_t f, e;
//初始化静态链表的指针域
for (int i = 0; i < sllist->len; ++i)
{
sllist->data[i].next = i + 1;
}
sllist->data[sllist->len].next = 0;
for (int i = 0; i < sllist->keyNum; ++i)
{
Distribute(sllist, i, &f, &e);
Collect(sllist, &f, &e);
}
}
void Radix_sort(SqList_t* list)
{
SLList_t sllist;
sllist.len = list->len;
sllist.keyNum = MAX_KEY_NUM;
//将数值转换为多关键字,存入静态链表
for (int i = 1; i < sllist.len + 1; ++i)
{
Key_t num = list->rec[i].key;
for (int j = 0; j < sllist.keyNum; j++)
{
sllist.data[i].keys[j] = num % 10;
num /= 10;
}
}
SLList_radix_sort(&sllist);
int adr[MAX_SIZE + 1];
adr[0] = 0;
for (int i = 1; i <= list->len; ++i)
{
adr[i] = sllist.data[adr[i-1]].next;
}
Rearrange(list, adr); //该函数是利用地址数组重排记录的实际物理地址 解释见内部排序的第一篇博客
}
设 n : 记 录 数 , d : 关 键 字 数 , r d : 基 数 设\ \ n:记录数, d:关键字数 , rd:基数 设 n:记录数,d:关键字数,rd:基数
∴ T ( n ) = O ( d ( n + r d ) ) \therefore T(n)=O(d(n+rd)) ∴T(n)=O(d(n+rd))可以看到,当 n n n很大时,这个时间复杂度接近于线性
需要 2 r d 2rd 2rd个队列指针以及 n n n个指针域
∴ S ( n ) = O ( r d + n ) \therefore S(n)=O(rd+n) ∴S(n)=O(rd+n)