对于基数排序的算法描述和C语言实现
对于一个关键字,将其看成由d个小关键字组成,然后每个关键字的范围中都有r个取值可能,称之为基数。例如,关键字是0~999的一个数字,则它有三个小关键字,每个关键字的范围是0~9,则按照最低位开始,将所有关键字分配到0~9这r个队列,然后收集在一起,进行三次就可以排好序。关键字是由5个字母组成的单词,则它有5个小关键字,每个关键字的范围是a~z,则从最低位开始将其分配到a~z这26个队列,然后收集在一起,进行5次就可以排好序。每次对于小关键字的收集,只用收集不用排序。这是基数排序的整体思路
实现基数排序时,第一个函数 void RadixPass(RcdType A[], RcdType B[], int n, int i),目的是将对数组A中记录的关键字的第 i 位计数,并按计数数组count的值,将数组A中的记录复制到数组B中。其中n表示记录数组中存储数据的个数,i 表示当前的位数,对其赋初值 bitsnum-1,表示其从关键字的最低为开始。然后首先对数组count[ ]初始化清零,然后k=1开始对记录数组从第一个数据开始遍历,统计关键字当前位0~1各占多少。其次对其进行累加,得到最终count[ ]数组。最后k = n,从右端开始向左遍历,为B数组赋值。第二个函数void RadixSort(SqList &L) 就是调用上一个函数,对顺序表进行基数排序。先进行一次排序后进入 if 语句判断是否排序完成,如果完成了则将排序后的结果在C中,复制到L.r中。如果没完成则再进行一次排序,然后循环。
#include<stdio.h>
/********************************************************************************
进行基数排序时对记录的数据类型要重新定义
*********************************************************************************/
#define MAX_NUM_OF_KEY 6 //关键字项数的最大值暂定为6
#define RADIX 10 //关键字基数,此为十进制整数的基数
#define MAXSIZE 10
typedef int keysType;
typedef struct {
keysType keys[MAX_NUM_OF_KEY];
char data;
int bitsnum; //关键字位数
}RcdType;
/********************************************************************************
对于数据元素是记录的顺序表的定义
*********************************************************************************/
typedef struct {
RcdType *r; //r[0]是哨兵单元,不记录数值 r[i]表示记录
int length; //表示顺序表的实时长度
}SqList;
/*********************************************************************************
建表
**********************************************************************************/
void InitList_L(SqList &L)
{ //定义过数据类型后引用时要先给它分配内存空间
L.r = new RcdType[MAXSIZE + 1];
L.length = 0; //初始化表长为0
L.r->bitsnum = 2; //关键字位数暂定为2
}
/**********************************************************************************
排序
***********************************************************************************/
void RadixPass(RcdType A[], RcdType B[], int n, int i)
{
//对数组A中记录的关键字的第 i 位计数,并按计数数组count的值,将数组A中的记录复制到数组B中
//形参中的 n 表示整个顺序表中记录数组的元素个数即存储数据的个数
//形参中的 i 表示关键字的位数,初始值位bitsnum-1,表示输入关键字时顺序输入,调用时表示从关键字的最低位开始
int j,k;
int count[RADIX]; //因为统计的是0~9中每一个基数在关键字中每一位出现的次数,所以count数组的大小位RADIX
for (j = 0; j < RADIX; j++)
count[j] = 0; //初始化对计数数组清零
for (k = 1; k <= n; k++)
count[A[k].keys[i]]++; //A[k].keys[i]表示A记录数组中第k个记录的关键字的i当前位是几,相当于遍历一遍
for (j = 1; j < RADIX; j++)
count[j] = count[j - 1] + count[j]; //累加操作
for (k = n; k >= 1; k--) //从右端开始复制记录
{
j = A[k].keys[i]; //对于A表,从最右端开始向左遍历,j取得记录关键字的 i 位
B[count[j]] = A[k]; //对于上一步遍历到的A[k],将其复制到B表中
count[j]--;
}
}
void RadixSort(SqList &L)
{
//对顺序表L进行基数排序
RcdType *C = new RcdType[L.length+1]; //开设同等大小的辅助空间用于复制数据
int i = L.r->bitsnum - 1; //对于 i 用于表示关键字的位数,由bitsnum-1可以表示 i 从最低为开始
int j;
while (i >= 0)
{
RadixPass(L.r, C, L.length, i); //对L.r进行一趟基数排序,排序结果存入C
i--; //一趟基数排序后,i 减一 表示位数向左移动一位
if (i >= 0)
{
RadixPass(C, L.r, L.length, i); //对C进行一趟基数排序,排序结果存入L.r
i--;
}
else
for (j = 1; j <= L.length; j++)
L.r[j] = C[j]; //排序后的结果在C中,复制到L.r中
}
}
void main()
{
int i, j, f = 0;
SqList L;
InitList_L(L);
char a[MAXSIZE] = { 'Y','L','V','X','O','L','!','I','U','E' };
int key[MAXSIZE*2] = { 0,3,0,5,0,7,0,2,0,6,0,1,1,0,0,4,0,9,0,8 };
for (i = 1; i <= MAXSIZE; i++)
{
L.r[i].data = a[i - 1];
for (j = 0; j < 2&&f<2*MAXSIZE; j++,f++)
L.r[i].keys[j] = key[f];
L.length++; //千万不要忘记每输入一个记录,表长要加一
}
printf("排序前顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
{
printf("%c ", L.r[i].data);
}
printf("\n");
RadixSort(L);
printf("排序后顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
printf("%c ", L.r[i].data);
}
对于本实验的总结反思
第一点,书上的算法操作过程没有考虑哨兵单元,但是最初定义数据结构时设置了r[0]这一哨兵单元,因此用C语言实现时,对于部分循环变量的范围控制,初始值控制要进行修改
第二点,如果实验最后编译成功,运行也能输出正确结果,但是系统却报错,显示已触发断点,(情况如下图),则大概率是内存溢出问题。根源在于运用new函数动态分布内存时,内存分配的少了,导致内存不够用,此时就要检查代码,着重检查new函数部分,看看是不是内存分配错误,然后适当调大内存。
本笔记所依据的教材为严薇敏版的《数据结构及应用算法教程》
所有代码在Visual Studio 2017上均可正常运行
如有错误欢迎指出