数据结构(C语言)代码实现(六)——单链表的实现

目录

参考、格式

头文件LinkList.h

一、将函数的小括号写成中括号

二、读取权限冲突

三、L->Last指针没有移动

四、函数指针的使用

头文件完整代码

测试函数(主函数)test.cpp

测试结果


参考、格式

数据结构课本2.3节(严蔚敏版)

头文件LinkList.h

由于本部分函数过多,这里只介绍自己在实现过程中出现的问题。

一、将函数的小括号写成中括号

E0254 不允许使用类型名-CSDN博客

二、读取权限冲突

C++读取访问权限冲突引发异常问题_引发了异常: 读取访问权限冲突。-CSDN博客

没有记录错误代码,不过编码过程中多次出现过这个问题,原因是指针未初始化(特别是函数内部)

三、L->Last指针没有移动

//算法2.20 插入元素 改编自算法2.9
Status List_Insert_L(LinkList& L, int i, ElemType e) {//在带头结点的单链线性表L的第i个元素之前插入元素e,1<=i<=L->len//更准确的说法是在第i个结点之后(包括头结点)插入结点。Link s = NULL, h = NULL;if (!LocatePos(L, i - 1, h))return ERROR;//i值不合法if (!MakeNode(s, e))return ERROR;//结点存储分配失败InsFirst(h, s);//对于从第i个结点开始的链表,第i-1个结点是它的头结点//基于课本中的这句注释,我觉得要实现的线性链表头结点和首元结点不一致if (i > L->len)L->tail = s;//若增加的结点位于尾部,应修改尾指针L->len++;//InsFirst函数没有让记录元素个数的变量增加return OK;
}

四、函数指针的使用

函数指针和指针函数用法和区别-CSDN博客

头文件完整代码

#pragma once
#include <cstdio>
#include <cstdlib>
#include <cstring>#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;//Status是函数的类型,其值是函数结果状态代码
typedef int ElemType;//-----线性链表的存储结构---------
//-----实现单向非循环动态链表-----
//-----头结点与首元结点不一致-----
typedef struct LNode {//结点类型ElemType data;struct LNode* next;
}LNode,*Link,*Position;typedef struct LinkNode{      //链表类型Link head, tail;  //分别指向线性链表中的头结点和最后一个结点int len;          //指示线性链表中数据元素的个数
}LinkNode,*LinkList;Status MakeNode( Link & p,ElemType e ){//分配由p指向的值为e的结点,并返回OK;若分配失败,则返回ERRORp = (Link)malloc(sizeof(LNode));if (!p)return ERROR;p->data = e;return OK;
}
void FreeNode(Link& p) {//相当于free函数重命名//释放p所指结点free(p);p = NULL;
}Status InitList(LinkList& L) {//构造一个空的线性链表LL = (LinkList)malloc(sizeof(LinkNode));if (!L)return ERROR;L->head = L->tail = (Link)malloc(sizeof(LNode));if (!L->head)return ERROR;L->head->data = 0;L->head->next = NULL;L->len = 0;return OK;
}Status DestroyList(LinkList& L) {//销毁线性链表L,L不再存在Link p;while (L->head) {//删除所有结点p = L->head;L->head = L->head->next;free(p);}free(L);L = NULL;return OK;
}Status ClearList(LinkList& L) {//将线性链表L重置为空表,并释放原链表的结点空间Link p = L->head;while (L->head->next) {//保留一个结点,让其作为头结点p = L->head->next;L->head->next = p->next;free(p);}L->tail = L->head;L->len = 0;return OK;
}Status InsFirst(Link h, Link s) {//已知h指向线性链表的头结点,将s所指结点插入到首元结点之前s->next = h->next;h->next = s;//这个函数没修改L->len的值,课本算法2.20需要补充L->len++。return OK;
}Status DelFirst(Link h, Link& q) {//已知h指向线性链表的头结点,删除链表中的首元结点并以q返回q = h->next;h->next = q->next;q->next = NULL;return OK;
}Status Append(LinkList& L, Link s) {//将指针s所指(彼此以指针相链)的一串结点链接在线性链表L的最后一个结点(用L->tail寻找)//之后,并改变链表L的尾指针指向新的尾结点while (s) {L->tail->next = s;L->tail = s;L->len++;s = s->next;}return OK;
}Status Remove(LinkList& L, Link& q) {//删除线性链表L中的尾节点并以q返回,改变链表L的尾指针指向新的尾结点if (!L->tail)return ERROR;Link p = L->head;while (p->next != L->tail)p = p->next;//寻找尾结点的上一个结点q = L->tail;L->tail = p;//改变尾结点L->tail->next = NULL;L->len--;return OK;
}Status InsBefore(LinkList& L, Link& p, Link s) {//已知p指向线性链表L中的一个结点,将s所指结点插入在p所指结点之前//并修改指针p指向新插入的结点Link q = L->head;while (q->next != p)q = q->next;//寻找结点q的上一个结点q->next = s;s->next = p;//插入sp = s;//修改指针pL->len++;return OK;
}Status InsAfter(LinkList& L, Link& p, Link s) {//已知p指向线性链表L中的一个结点,将s所指的结点插入在p所指结点之后//并修改指针p指向新插入的结点s->next = p->next;p->next = s;p = s;L->len++;return OK;
}Status SetCurElem(Link& p, ElemType e) {//已知p指向线性链表L中的一个结点,用e更新p所指结点中数据元素的值p->data = e;return OK;
}ElemType GetCurElem(Link p) {//已知p指向线性链表L中的一个结点,返回p所指结点中数据元素的值return p->data;
}Status ListEmpty(LinkList L) {//若线性链表L为空表,则返回TRUE,否则返回FALSEif (!L->len)return TRUE;elsereturn FALSE;
}int ListLength(LinkList L) {//返回线性链表L中元素个数return L->len;
}Position GetHead(LinkList L) {//返回线性链表L中头结点的位置return L->head;
}Position GetLast(LinkList L) {//返回线性链表L中尾结点的位置return L->tail;
}Position PriorPos(LinkList L, Link p) {//已知p指向线性链表L中的一个结点,返回p所指结点中的直接前驱的位置//若无前驱,则返回NULLLink q = L->head;if (p == L->head)return NULL;while (q->next != p)q = q->next;return q;
}Position NextPos(LinkList L, Link p) {//已知p指向线性链表L中的一个结点,返回p所指结点中的直接后继的位置//若无后继,则返回NULLreturn p->next;
}Status LocatePos(LinkList L, int i, Link& p) {//返回p指示线性链表L中第i个结点的位置并返回OK,i值不合法时返回ERROR//头结点当作第0结点,首元结点第1个,尾结点第(L->len)个if (i<0 || i>L->len)return ERROR;int j = 0;Link q = L->head;while (j < i) {q = q->next;++j;//j++也一样}p = q;return OK;
}//compare与visit都是函数指针,请看相关博客
Position LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType)) {//返回线性链表L中第1个与e满足函数compare()判定关系的元素的位置,//若不存在这样的元素,则返回NULLint i = 1;Link p = L->head->next;while (i <= L->len && !(*compare)(p->data, e)) {p = p->next;i++;}return p;
}//遍历链表
Status ListTraverse(LinkList L, void (*visit)(Link)) {//依次对L的每个元素调用函数visit()。一旦visit()失败,则操作失败。Link p = L->head;for (int i = 1;i <= L->len;i++) {p = p->next;visit(p);}printf("\n");return OK;
}
//上面的为线性链表的基本算法//算法2.20 插入元素 改编自算法2.9
Status List_Insert_L(LinkList& L, int i, ElemType e) {//在带头结点的单链线性表L的第i个元素之前插入元素e,1<=i<=L->len//更准确的说法是在第i个结点之后(包括头结点)插入结点。Link s = NULL, h = NULL;if (!LocatePos(L, i - 1, h))return ERROR;//i值不合法if (!MakeNode(s, e))return ERROR;//结点存储分配失败InsFirst(h, s);//对于从第i个结点开始的链表,第i-1个结点是它的头结点//基于课本中的这句注释,我觉得要实现的线性链表头结点和首元结点不一致if (i > L->len)L->tail = s;//若增加的结点位于尾部,应修改尾指针L->len++;//InsFirst函数没有让记录元素个数的变量增加return OK;
}//删除元素
Status List_Delete_L(LinkList& L, int i, ElemType& e) {Link s = NULL, h = NULL;if (!LocatePos(L, i - 1, h))return ERROR;//i值不合法if (i == L->len)L->tail = h;//若删除的结点位于尾部,应修改尾指针DelFirst(h, s);//对于从第i个结点开始的链表,第i-1个结点是它的头结点e = s->data;FreeNode(s);L->len--;return OK;
}//算法2.21 合并线性表 改编自算法2.12
Status MergeList_L(LinkList& La, LinkList& Lb, LinkList& Lc, int (*compare)(ElemType, ElemType)) {//已知单链线性表La和Lb的元素按值非递减排列。//归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列。if (!InitList(Lc))return ERROR;//存储空间分配失败Link ha = GetHead(La);Link hb = GetHead(Lb);//ha和hb分别指向La和Lb的头结点Link pa = NextPos(La, ha);Link pb = NextPos(Lb, hb);//pa和pb分别指向La和Lb中当前结点while (pa && pb) {//La和Lb均非空int a = GetCurElem(pa);int b = GetCurElem(pb);//a和b为两表中当前比较元素Link q = NULL;if ((*compare)(a, b) <= 0) {//a<=bDelFirst(ha, q);Append(Lc, q);pa = NextPos(La, ha);}else{//a>bDelFirst(hb, q);Append(Lc, q);pb = NextPos(Lb, hb);}}if (pa)Append(Lc, pa);//链接La中剩余结点else Append(Lc, pb);//链接Lb中剩余结点FreeNode(ha);FreeNode(hb);//释放La和Lb的头结点return OK;
}

测试函数(主函数)test.cpp

InBefore、InAfter、PriorPos这三个函数没有测试。

#include "LinkList.h"
Status comp(ElemType c1, ElemType c2) /* 数据元素判定函数(平方关系) */
{if (c1 == c2 * c2)return TRUE;elsereturn FALSE;
}int compare(ElemType a, ElemType b) /*数据元素判定函数(大小关系)*/
{return a - b;
}void visit(Link p) /* ListTraverse()调用的函数(类型要一致) */
{printf("%d ", p->data);
}void dbl(Link p) /* ListTraverse()调用的另一函数(元素值加倍) */
{p->data *= 2;
}int main()
{LinkList L, L1, L2;Status i;ElemType e0;int j;Link p, q;i = InitList(L);printf("初始化L后:L->len=%u\n", L->len);for (j = 1;j <= 5;j++) {i = List_Insert_L(L, 1, j);//有没有i=不影响结果,只有List_Insert(L,1,j);也行。}printf("在表头插入元素1到5,表L中元素为");i = ListTraverse(L, visit);i = ClearList(L);i = ListEmpty(L);if (i)printf("表L为空\n");for (j = 10;j > 5;j--) {i = List_Insert_L(L, 1, j);}printf("表L中元素为");i = ListTraverse(L, visit);printf("表L中元素个数为%d", ListLength(L));i = ListTraverse(L, dbl);printf("表L中元素为");i = ListTraverse(L, visit);i = List_Delete_L(L, 3, e0);printf("被删除的元素为%d\n", e0);printf("表L中元素为");i = ListTraverse(L, visit);for (j = 10;j > 5;j--) {i = List_Insert_L(L, 1, j);}printf("表L中元素为");i = ListTraverse(L, visit);printf("表L中元素个数为%d\n", ListLength(L));i = Remove(L, q);printf("被删除的元素为%d\n", q->data);i = SetCurElem(L->tail, 30);printf("表L中元素为");i = ListTraverse(L, visit);for (j = 3;j <= 4;j++) {p = LocateElem(L, j, comp);if (p)printf("表L中第1个为%d的平方的元素是%d\n", j, p->data);elseprintf("表L中没有%d的平方的元素\n", j);}i = InitList(L1);for (j = 13;j >= 3;j -= 2) {i = List_Insert_L(L1, 1, j);}printf("表L1中元素为");i = ListTraverse(L1, visit);i = MergeList_L(L, L1, L2, compare);printf("表L2中元素为");i = ListTraverse(L2, visit);//此时L与L1头结点均被删除i = DestroyList(L2);return 0;
}

测试结果

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

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

相关文章

嵌入式学习第三篇——51单片机

目录 1&#xff0c;嵌入式系统 1&#xff0c;嵌入式系统的定义 2&#xff0c;单片机的定义 2&#xff0c;51单片机 1&#xff0c;开发环境 2&#xff0c;开发板使用的基本思路 1&#xff0c;查看原理图&#xff0c;查看芯片手册 2&#xff0c;获得调用硬件的管…

博途PLC限幅器(SCL代码)

PLC限幅器详细介绍,可以参考下面文章: https://rxxw-control.blog.csdn.net/article/details/128701050https://rxxw-control.blog.csdn.net/article/details/128701050三菱PLC限幅器 https://rxxw-control.blog.csdn.net/article/details/135212965

自研人工智能小工具-小蜜蜂(国外ChatGpt的平替)

国内有非常多好用的人工智能工具&#xff0c;但均无法完全替代国外ChatGpt。 ChatGPT相较于其他国内工具的优势在于以下几点&#xff1a; 创新的语言生成能力&#xff1a;ChatGPT是由OpenAI开发的先进的自然语言生成模型&#xff0c;它采用了大规模的预训练和精细调整方法。因此…

初探unity中的ECS

ECS是一种软件架构模式&#xff0c;就像MVC一样。ECS最早在游戏《守望先锋》中提及到的相关链接。ECS具体是指实体&#xff08;entity&#xff09;、 组件&#xff08;component&#xff09;和系统&#xff08;system&#xff09;&#xff1a; 实体&#xff1a;实体是一个ID&a…

27. 云原生流量治理之kubesphere灰度发布

云原生专栏大纲 文章目录 灰度发布介绍灰度发布策略KubeSphere中恢复发布策略蓝绿部署金丝雀发布流量镜像 灰度发布实战部署自制应用金丝雀发布创建金丝雀发布任务测试金丝雀发布情况 蓝绿部署创建蓝绿部署测试蓝绿部署情况 流量镜像创建流量进行任务测试流量镜像情况 灰度发布…

【iOS ARKit】3D 人体姿态估计

与基于屏幕空间的 2D人体姿态估计不同&#xff0c;3D人体姿态估计是尝试还原人体在三维世界中的形状与姿态&#xff0c;包括深度信息。绝大多数的现有3D人体姿态估计方法依赖2D人体姿态估计&#xff0c;通过获取 2D人体姿态后再构建神经网络算法&#xff0c;实现从 2D 到 3D人体…

APK签名 v1、 v2、v3、v3.1、v4 解析

在 Android 应用签名中&#xff0c;V1 V2 V3 V4签名是不同的签名方案&#xff0c;具体描述如下&#xff1a; V1 签名&#xff08;JAR 签名&#xff09;&#xff1a;早期 Android 应用签名的基本形式&#xff0c;基于 Java 签名&#xff08;JAR 签名&#xff09;规范。它将应用…

<Linux> 进程信号

目录 一、信号概念 二、信号的作用 三、信号的特性 四、信号捕捉初识 五、信号产生 &#xff08;一&#xff09;通过终端按键产生信号 &#xff08;二&#xff09;硬件中断 &#xff08;三&#xff09;系统调用产生信号 1. kill 函数 2. raise 函数 3. abort 函数 …

Redis核心技术与实战【学习笔记】 - 22.浅谈Redis的ACID相关知识

概述 事务是数据库的一个重要功能。所谓的事务&#xff0c;就是指对数据进行读写的一系列操作。事务在执行时&#xff0c;会提供专门的属性保证&#xff0c;包括原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isol…

sentinel的Context创建流程分析

sentinel入门 功能 限流&#xff1a;通过限制请求速率、并发数或者用户数量来控制系统的流量&#xff0c;防止系统因为流量过大而崩溃或无响应的情况发生。 熔断&#xff1a;在系统出现故障或异常时将故障节点从系统中断开&#xff0c;从而保证系统的可用性。 降级&#xf…

性能测试工具架构

背景 性能测试工具&#xff08;LoadRunner为例&#xff09; 性能测试工具通常是指那些用来支持压力、负载测试&#xff0c;能够录制和生成脚本、设置和部署场景、产生并发用户和向系统施加持续压力的工具。 性能测试工具录制的是服务端与应用之间的通信数据&#xff0c;而不是…

【Spring】自定义注解 + AOP 记录用户的使用日志

目录 ​编辑 自定义注解 AOP 记录用户的使用日志 使用背景 落地实践 一&#xff1a;自定义注解 二&#xff1a;切面配置 三&#xff1a;Api层使用 使用效果 自定义注解 AOP 记录用户的使用日志 使用背景 &#xff08;1&#xff09;在学校项目中&#xff0c;安防平台…

【RT-DETR有效改进】 DySample一种超级轻量的动态上采样算子(上采样中的No.1)

👑欢迎大家订阅本专栏,一起学习RT-DETR👑 一、 本文介绍 本文给大家带来的改进机制是一种号称超轻量级且有效的动态上采样器——DySample。与传统的基于内核的动态上采样器相比,DySample采用了一种基于点采样的方法,相比于以前的基于内核的动态上采样器,DySample具…

整理:汉诺塔简析

大体上&#xff0c;要解决一个汉诺塔问题&#xff0c;就需要解决两个更简单的汉诺塔问题 以盘子数量 3 的汉诺塔问题为例 要将 3 个盘子从 A 移动到 C&#xff0c;就要&#xff1a; 将两个盘子从 A 移动到 B&#xff08;子问题 1&#xff09; 为了解决子问题 1&#xff0c;就…

clickhouse在MES中的应用-跟踪扫描

开发的MES&#xff0c;往往都要做生产执行跟踪扫描&#xff0c;这样会产生大量的扫描数据&#xff0c;用关系型数据库&#xff0c;很容易造成查询冲突的问题。 生产跟踪扫描就发生的密度是非常高的&#xff0c;每个零部件的加工过程&#xff0c;都要被记录下来&#xff0c;特别…

鲲鹏--垂直生态领导者

这是ren_dong的第24篇原创 1、概述 华为鲲鹏920&#xff1a;性能最高的ARM架构服务器芯片 鲲鹏是华为在芯片领域布局的重要一环 &#xff0c;是垂直生态的领导者&#xff0c;鲲鹏芯片诞生于2020年 &#xff0c;已获得ARMv8架构的永久授权&#xff0c;主要聚焦通用计算领域华为针…

typecho 在文章中添加 bilibili 视频

一、获取视频来源&#xff1a; 可以有2种方式来定位一个 bilibili 视频&#xff1a; 第一种是使用 bvid 参数定位第二种是使用 aid 参数定位 如何获取这两个参数&#xff1f; 首先我们可以看看 bilibili 网站中的视频页面链接其实可以分为两种&#xff1a; 第一种是类似&a…

建材智能工厂数字孪生可视化管控平台,推进建材行业数字化转型

建材智能工厂数字孪生可视化管控平台&#xff0c;推进建材行业数字化转型。随着科技的不断发展&#xff0c;数字化转型已经成为各行各业发展的重要趋势。在建材行业中&#xff0c;智能工厂和数字孪生技术的应用正在改变传统的生产模式和管理方式&#xff0c;为行业的数字化转型…

clr的执行模型-笔记

学习来源&#xff1a;《CLR via C by Jeffrey Richter 》第四版&#xff0c;第1章 clr的执行模型 1.C#编译生成执行程序集文件 编译文件的组成&#xff1a;pe32/pe32头&#xff0c;clr头&#xff0c;元数据&#xff0c;IL pe32/pe32头&#xff1a;windows标准执行文件头 cl…

DelayQueue的使用

具体思路&#xff1a; 在容器初始化的时候就创建出一个 延迟队列 然后项目启动后随即启动一个线程一直监听这个队列 手动调用接口往队列中添加任务 依赖 一个最简单的web的应用即可项目文件结构 第一步&#xff1a;在项目启动的时候就创建出一个延迟队列 Configuration publ…