对于一个关键字,将其看成由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对于本实验的总结反思#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上均可正常运行
如有错误欢迎指出



