基数排序
- 实现思想
- 基数排序也叫做多关键字排序,可以采用最低位优先法从最次位关键字到最主位关键字的顺序逐次排序。
- 最低位优先法:先从 kd-1 开始排序,再对 kd-2 进行排序,依次重复,直到对 k0 排序后便得到一个有序序列。k0 被称为最主位关键字,kd-1被称为最次位关键字。
- 时间复杂度
- r代表关键字的基数,d代表长度,n代表关键字的个数
- 最好情况:O(d(n+rd))
- 最坏情况:O(d(n+r))
- 平均时间复杂度:O(d(n+r))
- 空间复杂度
- O(n+rd)
- 稳定性
- 稳定
代码
#include <stdio.h>
#include "Status.h" //01 绪论//
#include "Scanf.c" //01 绪论//
/*
通常将关键字取值的数目称为基数,用 r 表示二2进制基数就是2,八进制基数就是8)
例如关键字是数字,无疑由0~9组成,基数就是10;如果关键字是字符串(字母组成),基数就是 26
*/
/*
char型数据与int型数据虽有区别,但也能相互转换。具体方法如下:
1、char型数字转换为int型,转换方法:a[i] - '0'
2、int类型转化为char类型,转化方法:a[i] + '0'
理由:
char 型数据在机器里通过 ASCII 码存储,也就是用一个整数存储的。譬如字符 '0 ',对应的 ASCII 码的十进制数是 48,因此强制转换成int后,是48而不是 0 .
字符’A’的ASCII码为65,然后是’B’ ‘C’ … ‘Z’
字符’a’的ASCII码为97,然后是’b’ ‘c’ … ‘z’
字符’0’的ASCII码为48,然后是’1’ ‘2’ ‘3’ … ‘9’
所以,要想把一个数字字符ch,转为一个整数,就是ch-‘0’当然ch-48也行
例如把’3’转换为3,就是’3’-‘0’
因为C和C++中,字符和整型是一样的,可以隐式转换。
*/
#define MAX_NUM_OF_KEY 8 //关键字项数的最大值
#define RADIX 10 //关键字基数,此时是十进制整数的基数
#define MAX_SPACE 10000
#define ord(ch) ((ch)-'0') //ord函数将char型数字转换为int型
#define succ(x) ((x)+1) //succ为求后继函数
/* 静态链表的结点类型 */
typedef char KeysType;
typedef struct
{
KeysType keys[MAX_NUM_OF_KEY]; //关键字
int next;
}SLCell;
/* 静态链表类型 */
typedef struct
{
SLCell r[MAX_SPACE]; //静态链表的可利用空间,r[0]为头结点
int keynum; //记录的当前关键字个数(就是当前所有的关键字中最大的关键字所包含的位数,例如最大关键字是百,说明所有keynum=3)
int recnum; //静态链表当前长度
}SLList;
/*
数组指针:
顾名思义,它是一个指针,是一个指向数组的一个指针。
举例:int (*p)[4];
指针数组
顾名思义,它是一个数组,是一个存放指针的数组。
函数指针:
顾名思义,它是一个指针,是一个存放函数地址的指针。
函数指针数组:
把函数的地址存到一个数组中,那这个数组就叫函数指针数组
指向函数指针数组的指针:
是一个指针 ,指针指向一个数组 ,数组的元素都是函数指针
*/
/* 指针数组类型 */
typedef int ArrType[RADIX]; //指针数组类型(用于记录各子序列的首尾位置)【我觉得数组里存放的并非C语法里的指针,而是int类型的数据,由于它代表的是位置,所以叫它“指针”】
/*
在计算机上实现基数排序时,为减少所需辅助存储空间,应采用链表作存储结构,即链式基数排序,具体作法为:
1、以静态链表存储待排记录,并令表头指针指向第一个记录;
2、“分配”时,按当前“关键字位”所取值,将记录分配到不同的“链队列” 中,每个队列中记录的“关键字位”相同;
3、“收集”时,按当前关键字位取值从小到大将各队列首尾相链成一个链表;
4、对每个关键字位均重复 2 和 3 两步。
*/
/* (01)创建静态链表L */
void CreateSLList(FILE *fp, SLList *L)
{
int i;
char s[1000];
Scanf(fp, "%d", &((*L).keynum)); //关键字个数
(*L).recnum = 0; //静态链表当前长度
while(Scanf(fp, "%s", &s)==1) //%s用来输出一个字符串
{
(*L).recnum++;
for(i=0; i<(*L).keynum; i++) //按低位到高位的顺序将关键字存储到keys
(*L).r[(*L).recnum].keys[(*L).keynum-1-i] = s[i]; //从r[1]开始存储(将高位放到靠后的位置;低位放到靠前的位置)
}
for(i=0; i<(*L).recnum; i++) //将L改造为静态链表(循环)
(*L).r[i].next = i + 1; //r[0]为头结点 ,令表头指针指向第一个记录
(*L).r[(*L).recnum].next = 0; //令最后一个元素指向头结点
}
/* (02)输出静态链表L中的关键字记录 */
void Traverse(SLList L)
{
int i, j;
for(i=L.r[0].next; i; i=L.r[i].next)
{
for(j=L.keynum-1; j>=0; j--)
printf("%c", L.r[i].keys[j]);
printf(" ");
}
printf("\n");
}
/* (03)对静态链表L作基数排序 */
/*
L是采用静态链表表示的顺序表。
对L作基数排序,使得L成为按关键字自小到大的有序静态链表,L.r[0]为头结点。
*/
void RadixSort(SLList *L)
{
int i;
ArrType f, e;
for(i=0; i<(*L).keynum; i++) //按最低位优先依次对各关键字进行分配和收集
{
Distribute((*L).r, i, f, e); //第i趟分配
Collect((*L).r, i, f, e); //第i趟收集
}
}
/* (04)分配算法,按第i个关键字keys[i]建立RADIX个子表,使同一子表中记录的keys[i]相同 */
/*
静态链表L的r域中记录已按(keys[0],...,keys[i-1])有序;
f[0..RADIX-1]和e[0..RADIX-1]分别指向各子表中的第一个和最后一个记录
【排序的分配算法,i表示分配的位次(是个位,十位还是百位),f始终指向各子序列中第一个记录的位置,e始终指向各子序列中最后一个记录的位置(基数是10,共有10个子序列,可看成10个桶)】
*/
void Distribute(SLCell r[], int i, ArrType f, ArrType e)
{
int j, p;
for(j=0; j<RADIX; j++) //各子表初始化为空表(初始化指针数组)
{
f[j] = 0;
e[j] = 0;
}
for(p=r[0].next; p; p=r[p].next) //遍历各个关键字
{
j = ord(r[p].keys[i]); //ord将记录中第i个关键字映射到[0..RADIX-1] (取出每个关键字的第i位,由于采用的是最低位优先法,所以,例如,第0位指的就是每个关键字的个位)
if(!f[j]) //如果指向该位数字的指针不存在,说明这是第一个关键字,直接记录该关键字的位置即可
f[j] = p;
else //如果存在,说明之前已经有同当前关键字相同位的记录,所以需要将其进行连接,将最后一个相同的关键字的next指针指向当前关键字所在的位置,同时令e[j]指向现在关键字所在的位置。
r[e[j]].next = p;
e[j] = p; //将p所指的结点插入第j个子表中 (p代表的是当前关键字在静态链表中的位置;如果之前已经有同该关键字相同位的记录,则令e[j]指向现在关键字所在的位置)
}
}
/* (05)收集算法,按keys[i]从小到大地将f[0..RADIX-1]所指各子表依次链接成一个链表 */
/*
e[0..RADIX-1]为各子表的尾指针
【基数排序的收集算法,即重新设置链表中各结点的尾指针】
*/
void Collect(SLCell r[], int i, ArrType f, ArrType e)
{
int j, t;
for(j=0; j<RADIX&&!f[j]; j=succ(j)) //找第一个非空子表,succ为求后继函数(从0开始遍历,查找头指针不为空的情况,为空表明该位没有该类型的关键字)
;
if(j<RADIX)
{
r[0].next = f[j]; //r[0].next指向第一个非空子表中第一个结点
t = e[j]; //找到尾指针的位置
while(j<RADIX)
{
for(j=succ(j); j<RADIX-1&&!f[j]; j=succ(j))
; //寻找下一个非空子表
if(f[j]&&j<=RADIX-1) //链接两个非空子表
{
r[t].next = f[j];
t = e[j];
}
}
r[t].next = 0; //t指向最后一个非空子表中的最后一个结点
}
}
int main(int argc, char *argv[])
{
SLList L;
printf("创建并输出一个任意序列...\n");
printf("函数 CreateSLList等 测试...\n"); //1、2.函数CreateSLList等测试
{
FILE *fp;
fp = fopen("TestData.txt", "r");
CreateSLList(fp, &L);
Traverse(L);
printf("\n");
}
PressEnter;
printf("函数 RadixSort等 测试...\n"); //3、4、5.函数RadixSort等测试
{
printf("将各关键字按递增顺序排列...\n");
RadixSort(&L);
Traverse(L);
printf("\n");
}
PressEnter;
return 0;
}