第 2 章 线性表 (设立尾指针的单循环链表(链式存储结构)实现)

news2024/11/26 2:52:50

1. 背景说明

循环链表(circular linked list),是另一种形式的链式存储结构。它的特点是表中最后一个结点的指针域指向头结点,

整个链表形成一个环。由此,从表中任一结点出发均可找到表中其他结点 。

2. 示例代码

1) status.h

/* DataStructure 预定义常量和类型头文件 */

#ifndef STATUS_H
#define STATUS_H

#define CHECK_NULL(pointer) if (!(pointer)) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_NULL_PTR); \
	return NULL; \
}

#define CHECK_RET(ret) if (ret != RET_OK) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ret); \
	return ret; \
}

#define CHECK_VALUE(value, ERR_CODE) if (value) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \
	return ERR_CODE; \
}

#define CHECK_FALSE(value, ERR_CODE) if (!(value)) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \
	return FALSE; \
} 

/* 函数结果状态码 */
#define TRUE 					1			/* 返回值为真 */
#define FALSE 					0			/* 返回值为假 */
#define RET_OK 					0			/* 返回值正确 */
#define INFEASIABLE    		   	2			/* 返回值未知 */
#define ERR_MEMORY     		   	3			/* 访问内存错 */
#define ERR_NULL_PTR   			4			/* 空指针错误 */
#define ERR_MEMORY_ALLOCATE		5			/* 内存分配错 */
#define ERR_NULL_STACK			6			/* 栈元素为空 */
#define ERR_PARA				7			/* 函数参数错 */
#define ERR_OPEN_FILE			8			/* 打开文件错 */
#define ERR_NULL_QUEUE			9			/* 队列为空错 */
#define ERR_FULL_QUEUE			10			/* 队列为满错 */
#define ERR_NOT_FOUND			11			/* 表项不存在 */
typedef int Status;							/* Status 是函数的类型,其值是函数结果状态代码,如 RET_OK 等 */
typedef int Bollean;						/* Boolean 是布尔类型,其值是 TRUE 或 FALSE */

#endif // !STATUS_H

2) cycleSingleList.h

/* 设立尾指针的单循环链表实现头文件 */

#ifndef CYCLESINGLELINKLIST_H
#define CYCLESINGLELINKLIST_H

#include "status.h"

typedef int ElemType;

typedef struct LNode {
	ElemType data;
	struct LNode *next;
} *LinkList;

/* 操作结果:构造一个空的线性表 L */
Status InitList(LinkList *L);

/* 操作结果:销毁线性表 L */
Status DestroyList(LinkList *L);

/* 初始条件:线性表 L 已存在
   操作结果:将 L 重置为空表 */
Status ClearList(LinkList *L);

/* 初始条件:线性表 L 已存在
   操作结果:若 L 为空表,则返回 TRUE,否则返回 FALSE */
Bollean ListEmpty(LinkList L);

/* 初始条件:L 已存在
   操作结果:返回 L 中数据元素个数 */
int ListLength(LinkList L);

/* 当第 i 个元素存在时,其值赋给 e 并返回 RET_OK, 否则返回 ERROR */
Status GetElem(LinkList L, int i, ElemType *e);

/* 初始条件:线性表 L 已存在,compare() 是数据元素判定函数
   操作结果:返回 L 中第 1 个与 e 满足关系 compare() 的数据元素的位序
   若这样的数据元素不存在,则返回值为 0 */
int LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType));

/* 初始条件:线性表 L 已存在
   操作结果:若 curr_e 是 L 的数据元素,且不是第一个,则用 pre_e 返回它的前驱
   否则操作失败,pre_e 无定义 */
Status PriorElem(LinkList L, ElemType curr_e, ElemType *pre_e);

/* 初始条件:线性表 L 已存在
   操作结果:若 curr_e 是 L 的数据元素, 且不是最后一个,则用 next_e 返回它的后继
   否则操作失败,next_e 无定义 */
Status NextElem(LinkList L, ElemType curr_e, ElemType *next_e);

/* 在 L 的第 i 个位置之前插入元素 e */
Status ListInsert(int i, ElemType e, LinkList *L);

/* 删除 L 的第 i 个元素,并由 e 返回其值 */
Status ListDelete(int i, LinkList *L, ElemType *e);

/* 初始条件: L 已存在
   操作结果: 依次对 L 的每个数据元素调用函数 vi()。一旦 vi() 失败,则操作失败 */
Status ListTraverse(LinkList L, void(*vi)(ElemType));

#endif // !CYCLESINGLELINKLIST_H

3) cycleSingleLink.c

/* 设立尾指针的单循环链表实现源文件 */

#include "cycleSingleLinkList.h"
#include <stdio.h>
#include <stdlib.h>


/* 辅助函数,创建一个新的节点 */
static LinkList MakeNewLNode(ElemType e)
{
	LinkList newLNode = (LinkList)malloc(sizeof(struct LNode));
	CHECK_NULL(newLNode)
	newLNode->data = e;
	newLNode->next = NULL;

	return newLNode;
}

/* 操作结果:构造一个空的线性表 L */
Status InitList(LinkList *L)
{
	*L = (LinkList)malloc(sizeof(struct LNode));
	CHECK_VALUE(!(*L), ERR_MEMORY_ALLOCATE)
	(*L)->next = *L;

	return RET_OK;
}

/* 操作结果:销毁线性表 L, *L 指的是尾指针 */
Status DestroyList(LinkList *L)
{
	LinkList p = (*L)->next;
	while (p != *L) {
		LinkList q = p->next;
		free(p);
		p = q;
	}

	free(*L);
	*L = NULL;

	return RET_OK;
}

/* 初始条件:线性表 L 已存在
   操作结果:将 L 重置为空表 */
Status ClearList(LinkList *L)
{
	*L = (*L)->next;
	LinkList p = (*L)->next;
	while (p != *L) {
		LinkList q = p->next;
		free(p);
		p = q;
	}

	(*L)->next = *L;

	return RET_OK;
}

/* 初始条件:线性表 L 已存在
   操作结果:若 L 为空表,则返回 TRUE,否则返回 FALSE */
Bollean ListEmpty(LinkList L)
{
	return (L->next == L) ? TRUE : FALSE;
}

/* 初始条件:L 已存在
   操作结果:返回 L 中数据元素个数 */
int ListLength(LinkList L)
{
	int length = 0;
	LinkList p = L->next;
	while (p != L) {
		++length;
		p = p->next;
	}

	return length;
}

/* 当第 i 个元素存在时,其值赋给 e 并返回 RET_OK, 否则返回 ERROR */
Status GetElem(LinkList L, int i, ElemType *e)
{
	LinkList p = L->next->next;
	CHECK_VALUE((i < 1 || i > ListLength(L)), ERR_PARA)
	int pos = 0;
	while (pos < i - 1) {
		p = p->next;
		++pos;
	}

	*e = p->data;

	return RET_OK;
}

/* 初始条件:线性表 L 已存在,compare() 是数据元素判定函数
   操作结果:返回 L 中第 1 个与 e 满足关系 compare() 的数据元素的位序
   若这样的数据元素不存在,则返回值为 0 */
int LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType))
{
	LinkList p = L->next->next;
	int pos = 0;
	while (p != L->next) {
		++pos;
		if (compare(p->data, e)) {
			return pos;
		}

		p = p->next;
	}

	return 0;
}

/* 初始条件:线性表 L 已存在
   操作结果:若 curr_e 是 L 的数据元素,且不是第一个,则用 pre_e 返回它的前驱
   否则操作失败,pre_e 无定义 */
Status PriorElem(LinkList L, ElemType curr_e, ElemType *pre_e)
{
	LinkList p = L->next->next;
	LinkList q = p->next;
	while (q != L->next) {
		if (q->data == curr_e) {
			*pre_e = p->data;
			return TRUE;
		}

		p = q;
		q = q->next;
	}

	return FALSE;
}

/* 初始条件:线性表 L 已存在
   操作结果:若 curr_e 是 L 的数据元素, 且不是最后一个,则用 next_e 返回它的后继
   否则操作失败,next_e 无定义 */
Status NextElem(LinkList L, ElemType curr_e, ElemType *next_e)
{
	LinkList p = L->next->next;
	while (p != L) {
		if (p->data == curr_e) {
			*next_e = p->next->data;
			return TRUE;
		}

		p = p->next;
	}

	return FALSE;
}

/* 在 L 的第 i 个位置之前插入元素 e */
Status ListInsert(int i, ElemType e, LinkList *L)
{
	CHECK_VALUE((i < 1 || i > ListLength(*L) + 1), ERR_PARA)
	LinkList p = (*L)->next;
	int pos = 0;
	while (pos < i - 1) {
		++pos;
		p = p->next;
	}

	LinkList newLNode = MakeNewLNode(e);
	CHECK_VALUE(!newLNode, ERR_MEMORY_ALLOCATE)
	newLNode->next = p->next;
	p->next = newLNode;
	if (p == *L) {
		*L = newLNode;
	}

	return RET_OK;
}

/* 删除 L 的第 i 个元素,并由 e 返回其值 */
Status ListDelete(int i, LinkList *L, ElemType *e)
{
	LinkList p = (*L)->next;
	CHECK_VALUE((i < 1 || i > ListLength(*L)), ERR_PARA)
	int pos = 0;
	while (pos < i - 1) {
		++pos;
		p = p->next;
	}

	LinkList q = p->next;
	p->next = q->next;
	*e = q->data;
	if (q == *L) {
		*L = p;
	}

	free(q);

	return RET_OK;
}

/* 初始条件: L 已存在
   操作结果: 依次对 L 的每个数据元素调用函数 vi()。一旦 vi() 失败,则操作失败 */
Status ListTraverse(LinkList L, void(*vi)(ElemType))
{
	LinkList p = L->next->next;
	while (p != L->next) {
		vi(p->data);
		p = p->next;
	}

	return RET_OK;
}

4) algorithm.h

/* 算法定义头文件 */
#ifndef ALGORITHM_H
#define ALGORITHM_H

#include "cycleSingleLinkList.h"

/* 算法,合并两个已知尾指针的循环链表 */
Status MergeList(const LinkList Lb, LinkList *La);

#endif // !ALGORITHM_H

5) algorithm.c

/* 算法实现源文件 */

#include "algorithm.h"
#include <stdio.h>
#include <stdlib.h>

/* 算法,合并两个已知尾指针的循环链表 */
Status MergeList(const LinkList Lb, LinkList *La)
{
	CHECK_VALUE(ListEmpty(Lb), ERR_NULL_PTR)
	CHECK_VALUE(ListEmpty(*La), ERR_NULL_PTR)
	LinkList p = (*La)->next;
	(*La)->next = Lb->next->next;
	free(Lb->next);
	Lb->next = p;
	*La = Lb;

	return RET_OK;
}

6) main.c

/* 入口程序源文件 */

#include "cycleSingleLinkList.h"
#include "algorithm.h"
#include <stdio.h>

void Visit(ElemType e);
Status Compare(ElemType e1, ElemType e2);

int main(void)
{
	LinkList L;
	Status ret = InitList(&L);
	CHECK_RET(ret)
	printf("LinkList L is %s\n", (ListEmpty(L) == TRUE) ? "empty" : "not empty");
	ListInsert(1, 3, &L);
	ListInsert(2, 5, &L);
	ElemType e;
	GetElem(L, 1, &e);
	printf("The length of L is %d, the value of 1th element is %d\n", ListLength(L), e);
	printf("The elements in L is: ");
	ListTraverse(L, Visit);
	putchar('\n');
	PriorElem(L, 5, &e);
	printf("The previous element of 5 is %d\n", e);
	NextElem(L, 3, &e);
	printf("The next element of 3 is %d\n", e);
	printf("LinkList L is %s\n", (ListEmpty(L) == TRUE) ? "empty" : "not empty");
	ret = LocateElem(L, 5, Compare);
	if (ret) {
		printf("The %dth element of L is %d\n", ret, 5);
	} else {
		printf("The element 5 is not exist in L.\n");
	}

	printf("Delete the 2th element of L\n");
	ret = ListDelete(2, &L, &e);
	if (ret == RET_OK) {
		printf("The element deleted is %d, Now the elements in L is: ", e);
		ListTraverse(L, Visit);
		putchar('\n');
	} else {
		printf("Delete element failed!\n");
	}

	printf("Clear L %s\n", (ClearList(&L) == RET_OK) ? "success" : "not success");
	printf("Now L is %s\n", (ListEmpty(L) == TRUE) ? "empty" : "not empty");
	printf("Destroy L %s\n", (DestroyList(&L) == TRUE) ? "success" : "not success");

	/* Algorithm Test */
	LinkList La, Lb;
	InitList(&La);
	InitList(&Lb);
	for (int i = 0; i < 5; ++i) {
		ListInsert(i + 1, i + 1, &La);
		ListInsert(i + 1, (i + 1) * 2, &Lb);
	}

	printf("After initialize La, La is: ");
	ListTraverse(La, Visit);
	putchar('\n');
	printf("After initialize Lb, Lb is: ");
	ListTraverse(Lb, Visit);
	putchar('\n');
	MergeList(Lb, &La);
	printf("After merge La and Lb, La is: ");
	ListTraverse(La, Visit);
	putchar('\n');
	DestroyList(&La);

	return 0;
}

void Visit(ElemType e)
{
	printf("%d ", e);
}

Status Compare(ElemType e1, ElemType e2)
{
	return (e1 == e2) ? TRUE : FALSE;
}

3. 输出示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/993323.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Parsing error: The keyword ‘const‘ is reserved

Parsing error: The keyword ‘const’ is reserved 在文件.eslintrc.js中 因为eslint默认审查的es5&#xff0c;需要明确让他审查es6.&#xff0c;所以需要配置parserOptions 配置如下: module.exports {"plugins": ["prettier"],"rules": {…

【自学开发之旅】Flask-标准化返回-连接数据库-分表-orm-migrate-增删改查(三)

业务逻辑不能用http状态码判断&#xff0c;应该有自己的逻辑判断。想要前端需要判断&#xff08;好多if…else&#xff09;&#xff0c;所以需要标准化&#xff0c;标准化返回。 json标准化返回: 最外面&#xff1a;data,message,code三个字段。 data&#xff1a;返回的数据 co…

【Unity基础】2.网格材质贴图与资源打包

【Unity基础】2.网格材质贴图与资源打包 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity基础系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;网格材质纹理 第一次接触3D物体的话&#xff0c;会觉得好神奇啊&#xff0c;这个物体究竟是由什么组…

教育志愿者的初心与担当

近日&#xff0c;有报道显示&#xff0c;在全球范围内&#xff0c;大约有6000万的儿童由于各种原因无法接受基础教育&#xff0c;其中非洲大陆的教育贫瘠现象尤为显著。然而&#xff0c;在这片土地上&#xff0c;有一位中国年轻志愿者Lily Zhu&#xff0c;以一种富有创新精神的…

Mysql中in和exists的区别 not in、not exists、left join的相互转换

文章目录 1. in 介绍1.1 in中数据量的限制1.2 null值不参与in或not in&#xff0c;也就是说in and not in 并不是全量值&#xff0c;排除了null值1.3 in的执行逻辑 2. exists介绍2.1 exists not exists 是全量数据2.2 exists的执行逻辑 3. 小表驱动大表的好处4. in、not in、e…

图像噪声--添加噪声

椒盐噪声 椒盐噪声就是给图片添加黑白噪点&#xff0c;椒指的是黑色的噪点(0,0,0),盐指的是白色的噪点(255,255,255)&#xff0c;通过num来控制噪声多少&#xff0c;值越大添加的噪声越多&#xff0c;图像损坏的更加严重。 void add_salt_pepper_noise(Mat& src,Mat& …

Mojo-SDK详细安装教程

Mojo-SDK安装 运行环境&#xff1a;windows11wsl2&#xff08;ubuntu1804&#xff09; 截至20230909&#xff0c;windows,mac系统暂时不支持 step1: Install VS Code, the WSL extension, and the Mojo extension. step2: Install Ubuntu 22.04 for WSL and open it. step…

Vue el-table 重置按钮设计模板

vue 文件演示模板 <template><el-button icon"el-icon-refresh" size"large" click"resetFunction">重置</el-button><el-tableheight"450"v-loading"loading":data"dataList":row-key&quo…

U盘插上就让格式化是坏了吗?数据怎么恢复

U盘插上就让格式化是坏了吗&#xff1f;当您遇到U盘插上后提示需要格式化的情况时&#xff0c;不要慌张。这种情况并不一定意味着U盘已经坏了。下面我们一起来了解下如何恢复里面的数据&#xff0c;并解决U盘提示格式化的问题。 U盘一插上就提示格式化是什么原因 许多人可能会有…

个人微信多账号聚合管理如何实现?

在日常工作中&#xff0c;您是否会遇到以下问题&#xff1a; 微信号多&#xff0c;需反复切换设备及账号&#xff0c;工作效率低。 无法快速响应客户消息&#xff0c;客户体验感差。 无法随时掌握员工与客户沟通情况&#xff0c;员工沟通质量难以控制&#xff0c;管理难。 员…

Spring系列文章:面向切面编程AOP

一、代理模式 1、代理模式使用场景引入 ⽣活场景1&#xff1a;⽜村的⽜⼆看上了隔壁村⼩花&#xff0c;⽜⼆不好意思直接找⼩花&#xff0c;于是⽜⼆找来了媒婆王妈妈。这 ⾥⾯就有⼀个⾮常典型的代理模式。⽜⼆不能和⼩花直接对接&#xff0c;只能找⼀个中间⼈。其中王妈妈是…

OpenCV 11(图像金字塔)

一、 图像金字塔 **图像金字塔**是图像中多尺度表达的一种&#xff0c;最主要用于图像的分割&#xff0c;是一种以多分辨率来解释图像的有效但概念简单的结构。简单来说, 图像金字塔是同一图像不同分辨率的子图集合. 图像金字塔最初用于机器视觉和图像压缩。其通过梯次向下采…

shell知识点复习

1、shell能做什么&#xff08; Shell可以做任何事(一切取决于业务需求) &#xff09; 自动化批量系统初始化程序 自动化批量软件部署程序 应用管理程序 日志分析处理程序 自动化备份恢复程序 自动化管理程序 自动化信息采集及监控程序 配合Zabbix信息采集 自动化扩容 2、获取当…

淘宝双11数据分析与预测课程案例中(林子雨)错误点总结

问题一&#xff1a;可视化代码中男女买家各个年龄段对比散点图中数值不显示以及坐标不正确问题如下图 解决方法&#xff1a; 1修改坐标 2修改数值 修改后散点图 问题二&#xff1a;各省份的总成交量对比中地图显示不出来 有时间再写

海量小文件传输对于企业选用文件传输软件的重要意义

在当前的商业环境中&#xff0c;数据具有极其重要的作用&#xff0c;是企业竞争的核心要素。随着互联网、物联网和云计算等技术的快速发展&#xff0c;数据的类型和规模变得越来越多样。在这其中&#xff0c;海量小文件作为一种普遍而重要的数据形式&#xff0c;扮演着连接信息…

新知同享 | Mobile 开发轻松跨屏,高效构建

谷歌致力于帮助开发者 更快、更轻松地打造高质量的移动体验 一起来看 2023 Google 开发者大会上 Mobile 开发值得重点关注的成果与更新 了解如何提高平台及应用质量 提升开发效率 使多设备开发体验更流畅 实现轻松跨屏&#xff0c;高效构建 精彩大会现场一览 用户对跨屏幕体验…

在k8s中创建ConfigMap的四种方式与初识helm包管理工具

非敏感数据&#xff0c;比如应用的配置信息&#xff0c;则可以用ConfigMap 创建configmap四种方式 &#xff08;1&#xff09;通过--from-literal&#xff1a; kubectl create configmap myconfigmap --from-literalconfig1xxx --from-literalconfig2yyy 每个--from-literal…

Revit SDK 介绍:Ribbon 界面

前言 Revit 通过 API 将完整的 Ribbon 做了保留&#xff0c;同时这些菜单按钮也可以和相应的命令绑定。 内容 运行效果如下所示&#xff1a; 菜单特写&#xff1a; Ribbon Sample 整体是 API 暴露出来的一个 RibbonPanel&#xff0c;对应的接口&#xff1a; namespace Au…

dll文件反编译源代码 C#反编译 dotpeek反编译dll文件后export

目录 背景下载安装dotpeek导入dll文件export导出文件参考 背景 项目合作的时候&#xff0c;使用前人的或者其他部门dll文件直接在机台运行&#xff0c;会出现很多问题&#xff0c;逻辑&#xff0c;效率等等&#xff0c;此时我们可以选择对他们的代码进行反编译和重构&#xff…

递归算法学习——被围绕的区域,太平洋大西洋流水问题

目录 ​编辑 一&#xff0c;被围绕的区域 1.题意 2.解释 3.题目接口 4.解题思路及代码 二&#xff0c;太平洋大西洋流水问题 1.题意 2.解释 3.题目接口 4.解题思路及代码 一&#xff0c;被围绕的区域 1.题意 给你一个 m x n 的矩阵 board &#xff0c;由若干字符 X 和…