链式基数排序(第十章 P286 算法10.15,10.16,10.17)

2023-11-09

链式基数排序

 

概述:

 

基数排序(radix sort),属于“分配式排序”(distribution sort)。

基数排序也叫做多关键字排序,基数排序是一种借助“多关键字排序”的思想来实现“单关键字排序”的内部排序算法。

多关键字排序的方法:

n 个记录的序列 {R1, R2, …, Rn} 对关键字 (Ki0, Ki1, …, Ki(d-1) ) 有序是指:对于序列中任意两个记录 Ra 和 Rb  (1≤a < b≤n) 都满足下列(词典)有序关系:(Ka0, Ka1, …, Ka(d-1) ) <  (Kb0, Kb1, …, Kb(d-1) ) ,其中:K0  被称为最主位关键字, K(d-1)  被称为最次位关键字。多关键字排序按照从最主位关键字到最次位关键字或从最次位关键字到最主位关键字的顺序逐次排序,分两种方法:

最高位优先法(Most Significant Digit first),简称 MSD 法:先按 k0 排序分组,同一组中记录,关键字 k0 相等,再对各组按 k1 排序分成子组,之后,对后面的关键字继续这样的排序分组,直到按最次位关键字  kd  对各子组排序后,再将各组连接起来,便得到一个有序序列。

最低位优先法(Least Significant Digit first),简称 LSD 法:先从 k(d-1) 开始排序,再对 k(d-2) 进行排序,依次重复,直到对 k0 排序后便得到一个有序序列。

注意一点:LSD的基数排序适用于位数少的数列,如果位数多的话,使用MSD的效率会比较好。

 

 

排序过程:

注:这里使用LSD法进行排序

例:对于关键字序列 { 278, 109, 063, 930, 589, 184, 505, 269, 008,083 } 进行基数排序 。

可以将每个关键字 K 看成由三个单关键字组成,即 K= k1k2k3, 每个关键字的取值范围为 0≤ki≤9,所以每个关键字可取值的数目为 10。通常将关键字取值的数目称为基数,用 r 表示,在本例中 r =10。

对于关键字序列(AB, BD, ED)可以将每个关键字看成是由二个单字母关键字组成的复合关键字,并且每个关键字的取值范围为 “A~Z”,所以关键字的基数 r = 26。

对于由 d 位关键字组成的复合关键字,需要经过d 趟的“分配”与“收集”。 因此,若 d 值较大,基数排序的时间效率就会随之降低。

 

在计算机上实现基数排序时,为减少所需辅助存储空间,应采用链表存储 n 个待排记录,即链式基数排序。因为每个桶内的元素个数是未知的,所以需要借助链表结构来实施分配时向桶内仍记录的过程。

在每一趟分配进行时,改变记录的指针值,将链表中的记录分配到 10 个链队列中去,其中 f[i] 和 e[i] 分别为第 i 个队列的头指针和尾指针。具体作法为:

1、以静态链表存储待排记录,并令表头指针指向第一个记录; 

2、“分配” 时,按当前“关键字位”所取值,将记录分配到不同的 “链队列” 中,每个队列中记录的 “关键字位” 相同;

3、“收集”时,按当前关键字位取值从小到大将各队列首尾相链成一个链表; 

4、对每个关键字位均重复 2 和 3 两步。 

 

  • 初始转态

  • 首先按照各个数据的个位数字分配到0-9的10个区间内,第一趟分配之后结果如下

  • 分配结束后。接下来将所有空间中的数据按照序号由小到大依次重新收集起来,得到如下仍然无序的数据序列:

  • 这次按照十位数字进行分配,步骤同上,第二趟分配之后结果如下

  • 分配结束后。接下来将所有空间中的数据按照序号由小到大依次再次收集起来,得到如下仍然无序的数据序列

  • 这次按照百位数字进行分配,步骤同上,第三趟分配之后结果如下

 

  • 分配结束后。接下来将所有空间中的数据按照序号由小到大依次再次收集起来,得到有序序列。

 

 

算法性能:

借助桶编号(键)经过多次分配和采集,最终得到一个有序序列,在这个算法排序过程中,没有经过任何记录的比较,因此基数排序是很独特的排序算法。

时间复杂度:

待排序列为n个记录,d个关键码,关键码的取值范围为 r,其中,一趟分配时间复杂度为 O(n),一趟收集时间复杂度为O(r),共进行 d 趟分配和收集,所以链式基数排序的时间复杂度为 O( d·(n+r) ) 。

注意:这不是说这个时间复杂度一定优于O(n·log(n)),因为 d 的大小一般会受到 n 的影响。 

空间复杂度:

(2r 个队列指针 + n 个指针域空间),因为一个桶本质是一个链式队列,一共 r 个桶,每个队列有队头和队尾两个指针,就是2r 个队列指针。又原来的待排序列是一个单链表,那么自然需要 n 个next指针控件。

 

 

 

代码:

 

#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */

typedef int InfoType; /* 定义其它数据项的类型 */
typedef int KeyType; /* 定义RedType类型的关键字为整型 */
typedef struct
{
	KeyType key; /* 关键字项 */
	InfoType otherinfo; /* 其它数据项 */
}RedType; /* 记录类型(同c10-1.h) */
typedef char KeysType; /* 定义关键字类型为字符型 */



/* ---------------------------    基数排序的数据类型     ------------------------------*/


#define MAX_NUM_OF_KEY 8 /* 关键字项数的最大值 */
#define RADIX 10 /* 关键字基数,此时是十进制整数的基数 */
#define MAX_SPACE 1000
typedef struct
{
	KeysType keys[MAX_NUM_OF_KEY]; /* 关键字 */
	InfoType otheritems; /* 其它数据项 */
	int next;
}SLCell; /* 静态链表的结点类型 */

typedef struct
{
	SLCell r[MAX_SPACE]; /* 静态链表的可利用空间,r[0]为头结点 */
	int keynum; /* 记录的当前关键字个数 */
	int recnum; /*  静态链表的当前长度 */
}SLList; /* 静态链表类型 */

typedef int ArrType[RADIX]; /* 指针数组类型 */


/* ------------------------------------------------------------------------------------------*/



void InitList(SLList *L, RedType D[], int n)
{ /* 初始化静态链表L(把数组D中的数据存于L中) */
	char c[MAX_NUM_OF_KEY], c1[MAX_NUM_OF_KEY];
	int i, j, max = D[0].key; /* max为关键字的最大值 */
	for (i = 1; i < n; i++)
		if (max < D[i].key)
			max = D[i].key;
	(*L).keynum = (int)(ceil(log10(max)));
	(*L).recnum = n;
	for (i = 1; i <= n; i++)
	{
		(*L).r[i].otheritems = D[i - 1].otherinfo;
		itoa(D[i - 1].key, c, 10); /* 将10进制整型转化为字符型,存入c */
		for (j = strlen(c); j < (*L).keynum; j++) /* 若c的长度<max的位数,在c前补'0' */
		{
			strcpy(c1, "0");
			strcat(c1, c);
			strcpy(c, c1);
		}
		for (j = 0; j < (*L).keynum; j++)
			(*L).r[i].keys[j] = c[(*L).keynum - 1 - j];
	}
}

int ord(char c)
{ /* 返回k的映射(个位整数) */
	return c - '0';
}

void Distribute(SLCell r[], int i, ArrType f, ArrType e) /* 算法10.15 */
{ /* 静态键表L的r域中记录已按(keys[0],...,keys[i-1])有序。本算法按 */
  /* 第i个关键字keys[i]建立RADIX个子表,使同一子表中记录的keys[i]相同。 */
  /* f[0..RADIX-1]和e[0..RADIX-1]分别指向各子表中第一个和最后一个记录 */
	int j, p;
	for (j = 0; j < RADIX; ++j)
		f[j] = 0; /* 各子表初始化为空表 */
	for (p = r[0].next; p; p = r[p].next)
	{
		j = ord(r[p].keys[i]); /* ord将记录中第i个关键字映射到[0..RADIX-1] */
		if (!f[j])
			f[j] = p;
		else
			r[e[j]].next = p;
		e[j] = p; /* 将p所指的结点插入第j个子表中 */
	}
}

int succ(int i)
{ /* 求后继函数 */
	return ++i;
}

void Collect(SLCell r[], ArrType f, ArrType e)
{ /* 本算法按keys[i]自小至大地将f[0..RADIX-1]所指各子表依次链接成 */
  /* 一个链表,e[0..RADIX-1]为各子表的尾指针。算法10.16 */
	int j, t;
	for (j = 0; !f[j]; j = succ(j)); /* 找第一个非空子表,succ为求后继函数 */
	r[0].next = f[j];
	t = e[j]; /* r[0].next指向第一个非空子表中第一个结点 */
	while (j < RADIX - 1)
	{
		for (j = succ(j); j < RADIX - 1 && !f[j]; j = succ(j)); /* 找下一个非空子表 */
		if (f[j])
		{ /* 链接两个非空子表 */
			r[t].next = f[j];
			t = e[j];
		}
	}
	r[t].next = 0; /* t指向最后一个非空子表中的最后一个结点 */
}

void printl(SLList L)
{ /* 按链表输出静态链表 */
	int i = L.r[0].next, j;
	while (i)
	{
		for (j = L.keynum - 1; j >= 0; j--)
			printf("%c", L.r[i].keys[j]);
		printf(" ");
		i = L.r[i].next;
	}
}

void RadixSort(SLList *L)
{ /* L是采用静态链表表示的顺序表。对L作基数排序,使得L成为按关键字 */
  /* 自小到大的有序静态链表,L.r[0]为头结点。算法10.17 */
	int i;
	ArrType f, e;
	for (i = 0; i < (*L).recnum; ++i)
		(*L).r[i].next = i + 1;
	(*L).r[(*L).recnum].next = 0; /* 将L改造为静态链表 */
	for (i = 0; i < (*L).keynum; ++i)
	{ /* 按最低位优先依次对各关键字进行分配和收集 */
		Distribute((*L).r, i, f, e); /* 第i趟分配 */
		Collect((*L).r, f, e); /* 第i趟收集 */
		printf("第%d趟收集后:\n", i + 1);
		printl(*L);
		printf("\n");
	}
}

void print(SLList L)
{ /* 按数组序号输出静态链表 */
	int i, j;
	printf("keynum=%d recnum=%d\n", L.keynum, L.recnum);
	for (i = 1; i <= L.recnum; i++)
	{
		printf("keys=");
		for (j = L.keynum - 1; j >= 0; j--)
			printf("%c", L.r[i].keys[j]);
		printf(" otheritems=%d next=%d\n", L.r[i].otheritems, L.r[i].next);
	}
}

void Sort(SLList L, int adr[]) /* 改此句(类型) */
{ /* 求得adr[1..L.length],adr[i]为静态链表L的第i个最小记录的序号 */
	int i = 1, p = L.r[0].next;
	while (p)
	{
		adr[i++] = p;
		p = L.r[p].next;
	}
}

void Rearrange(SLList *L, int adr[]) /* 改此句(类型) */
{ /* adr给出静态链表L的有序次序,即L.r[adr[i]]是第i小的记录。 */
  /* 本算法按adr重排L.r,使其有序。算法10.18(L的类型有变) */
	int i, j, k;
	for (i = 1; i < (*L).recnum; ++i) /* 改此句(类型) */
		if (adr[i] != i)
		{
			j = i;
			(*L).r[0] = (*L).r[i]; /* 暂存记录(*L).r[i] */
			while (adr[j] != i)
			{ /* 调整(*L).r[adr[j]]的记录到位直到adr[j]=i为止 */
				k = adr[j];
				(*L).r[j] = (*L).r[k];
				adr[j] = j;
				j = k; /* 记录按序到位 */
			}
			(*L).r[j] = (*L).r[0];
			adr[j] = j;
		}
}

#define N 10
void main()
{
	RedType d[N] = { {278,1},{109,2},{63,3},{930,4},{589,5},{184,6},{505,7},{269,8},{8,9},{83,10} };
	SLList l;
	int *adr;
	InitList(&l, d, N);
	printf("排序前(next域还没赋值):\n");
	print(l);
	RadixSort(&l);
	printf("排序后(静态链表):\n");
	print(l);
	adr = (int*)malloc((l.recnum) * sizeof(int));
	Sort(l, adr);
	Rearrange(&l, adr);
	printf("排序后(重排记录):\n");
	print(l);
}

运行结果:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

链式基数排序(第十章 P286 算法10.15,10.16,10.17) 的相关文章

  • python使用matplotlib绘制折线图

    python使用matplotlib绘制折线图 Python绘图需要下载安装matplotlib模块 它是一个数学绘图库 我们将使用它来制作简单的图表 一 绘制单条折线图 import matplotlib pyplot as plt pl
  • 如何看论文(一)

    论文初步 开个头 即将进入研究生的大门 开始研究生的学习生活 将要面对成堆的论文 组会 以及等等 才发现最基础的看论文 也只是在大四毕设的时候粗略地尝试过几篇 离真正的看论文还差得很远 并且 在研究生阶段 按我的理解 看懂一篇文章没什么 讲
  • Ubuntu忘记密码(五个小步骤)

    Ubuntu忘记密码 五个小步骤 可能用到的操作 按键 鼠标操作 作用 进入虚拟机屏幕 点击 鼠标焦点在虚拟机中 接下来的操作都在虚拟机中响应 退出虚拟机屏幕 ctrl alt 将鼠标焦点从虚拟机中移除 回到主屏幕 步骤一 重启虚拟机 注意
  • 图形学实验四线段裁剪算法

    实验四 线段裁剪算法 实验类型 设计型 实验学时 2实验要求 必修 一 实验目的 了解二维图形裁剪的原理 点的裁剪 直线的裁剪 多边形的裁剪 利用VC OpenGL实现直线的裁剪算法 二 实验内容 1 理解直线裁剪的原理 编码裁剪算法 梁友

随机推荐

  • 采用python编写微信自动回复程序(基于图灵机器人)

    采用python编写微信自动回复程序 基于图灵机器人 写在开头 注册CSDN这么久 第一次发博客 难免有写得不明白的地方 请读者们谅解 一 要实现微信自动回复 需要如下准备 1 注册一个图灵机器人 现在是要收费的 不过一个月的费用也不是很贵
  • git中关于用户信息的命令

    一 前言 工作中需要查看git的一些用户信息 现将其记录如下 二 相关命令 查看当前项目的用户信息 该信息保存在项目下面隐藏文件夹 git config文件中 查看用户名称 git config user name 查看用户邮箱 git c
  • 通过Valgrind的Massif工具进行C++内存使用分析

    关于Valgrind的简介可以参考 https blog csdn net fengbingchun article details 50196189 Valgrind在Ubuntu上的安装可以参考 https blog csdn net
  • 【ARM】使用Ubuntu-base构建根文件系统

    使用Buildroot构建根文件系统 介绍 资源下载 配置根文件系统 设置软件源 安装必要软件 添加新用户 设置主机名称和本机IP 设置终端串口 网络DHCP FTP服务器搭建 串口无法登录 开机启动信息显示 Failed to inser
  • 硕士毕业生找工作经验体会(怎样才能说服你面前的HR)

    下个月就要离开交大了 这个我呆了将近7年的地方 最后想留下一点关于找工作方面的经验体会 从05年考研结束的时候开始找工作 之后知道研究生录取之后找实习 一直到07年正式找工作 期间我接触过很多行业 很多人 很多职位 从一开始 我就听无数人在
  • JavaEE 笔记01: 基于Tomcat, Servlet, JSP的简单作业管理系统

    基于Tomcat Servlet JSP的简单作业管理系统 目录 基于Tomcat Servlet JSP的简单作业管理系统 前言 2020年3月25日更新 2020年3月26日更新 2020年4月8日更新 2020年4月16日更新 202
  • 深聊性能测试,从入门到放弃之:Locust性能自动化(六)自定义生成负载图形形状

    自定义峰值形状 1 引言 2 定义 2 1 列举实例 2 2 如何继承 2 3 方法使用 3 代码实战 3 1 时间峰值 3 2 双波形 3 3 基于时间阶段 3 4 逐步加载 1 引言 今天分享的这部分内容 应该算是Locust的进阶篇
  • c++迭代器失效

    下面材料整理自Internet 著作 STL中的容器按存储方式分为两类 一类是按以数组形式存储的容器 如 vector deque 另一类是以不连续的节点形式存储的容器 如 list set map 在使用erase方法来删除元素时 需要注
  • Ubuntu 设置时区

    我们要设置成 CST 时区 以保证正确地显示日期 时间 我们常看到的时区有如下几个 PST 美国太平洋标准时间 PST GMT 8 GMT 格林尼治平均时间 等同于英国伦敦本地时间 UTC 通用协调时间 UTC GMT CST 北京时间 北
  • MVC 网上招聘系统的设计与实现java jsp 程序设计 课程设计 毕业设计-附源码02135

    因上传问题 只上传了文案 图片未上传 网上招聘系统的设计与实现 摘 要 随着时代的发展 中国的互联网技术愈加成熟 已经有越来越多的社会群体开始学会使用互联网技术 整个社会正在朝着智能化 信息化的方向前进 有了互联网 用户便可以足不出户地利用
  • 【转载】版本管理软件综述(VSS及其他)

    版本管理软件综述 VSS的使用 http www cnblogs com liuchaogege p 4465652 html 什么是版本控制 1 怎样对研发项目进行整体管理 2 项目开发小组的成员之间如何以一种有效的机制进行协调 3 如何
  • Latex图片格式——从png,jpg,jpeg等导出到eps

    Latex图片格式 从png jpg jpeg等导出到eps Windows 在安装了texlive的情况下 应该都安装了 不然怎么编译latex文档嘞 在图片文件夹运行cmd 输入 bmeps c test png test eps 完成
  • vue el-table展开需要绑定row-key

  • openGLES3.0编程指南源码运行

    前言 openGLES3 0编程指南随书源码环境配置和例子运行 在这篇文章中 笔者给出了官网例子配置和运行 但是我自己新建的单独工程源码正确 但依然无法运行程序 遇到的坑 印象深刻 记录一下 错误做法 openGL ES Emulator
  • windows powershell 里怎么从C盘跳到D盘?

    直接在C根目录时输入d 进入其他盘同理 PS C gt d
  • 关于SAR的研究热点——几点思考

    关于SAR的研究热点 几点思考 SAR研究热点之一 新体制论证 SAR系统设计追求的目标 图像质量高 空间和辐射分辨率高 成像幅宽大 具备多模式 扫描 可变入射角条带 斜视 聚束 多波段 全极化 三维成像 动目标检测与成像能力 对平台运动姿
  • 今日头条自媒体矩阵运营攻略

    你今天通常做什么 许多人用它来娱乐八卦 寻找乐趣 并打发时间 但对于媒体和品牌来说 它是一个非常好的操作平台 基于媒体矩阵标题数量的改进及其独特的推荐机制 公司传播品牌 打造个人品牌非常友好 今天我们讨论了标题号如何快速建立个人品牌的综合潜
  • Hexo一些实用的插件

    Hexo的插件真是个好东西 一开始部署博客的时候并没有太在意插件的问题 毕竟觉得博客主题自带的插件挺全面的 足够使用了 但是用久了总是会腻 就想着静态博客能不能整一些新操作 即使只是添加点小功能 于是就翻了翻 Hexo 的插件目录 挑了些比
  • Android BLE学习笔记

    http blog csdn net xiaoyaoyou1212 article details 51854454 个人网站 http www xiaoyaoyou1212 com 欢迎吐槽围观 前言 本文主要描述Android BLE的
  • 链式基数排序(第十章 P286 算法10.15,10.16,10.17)

    链式基数排序 概述 基数排序 radix sort 属于 分配式排序 distribution sort 基数排序也叫做多关键字排序 基数排序是一种借助 多关键字排序 的思想来实现 单关键字排序 的内部排序算法 多关键字排序的方法 n 个记