数据结构 基数排序

对于基数排序的算法描述和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上均可正常运行

如有错误欢迎指出

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值