单链表逻辑结构梳理

发布时间:2026/10/29 22:23:06

单链表逻辑结构梳理 1.1链表的概念及结构概念链表是一种物理存储结构上的非连续非连续的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表的种类包含单向双向带头不带头循环非循环。在八种链表结构中最常用的包含无头单向非循环链表以及带头双向循环链表。无头单项非循环链表结构简单一般不会单独来存储数据。实际中更多是作为其他数据结构的子结构入哈希桶图的邻接表等等带头双向循环链表结构最复杂一般用在单独存储数据。实际中使用的链表数据结构都是带头双向循环链表。虽然这个结构复杂但是使用代码以后会发现该结构会带来很多优势。在SList.h该文件中写文件声明以及结构体的定义#includestdlib.h #includeassert.h #includestdio.h //定义结点的结构 //数据指向下一个节点的指针 typedef int SLDataType; typedef struct SListNode { SLDataType data; struct SListNode* next; }SLTNode; void SLTPrint(SLTNode*pphead); //尾插 void SLTPushBack(SLTNode**pphead, SLDataType x); //头插 void SLTPushFront(SLTNode**pphead, SLDataType x); void SLTPopBack(SLTNode** pphead); //头删 void SLPopFront(SLTNode** pphead); //查找 SLTNode* SLTFind(SLTNode* phead, SLDataType x); //在指定位置之前插入数据 void SLTInsert(SLTNode**pphead, SLTNode* pos, SLDataType x); //在指定位置之后插入数据 void SLTInsertAter(SLTNode* pos, SLDataType x); //删除pos节点 void SLTErase(SLTNode** pphead, SLTNode* pos); // 删除pos 之后的节点 void SLTEraseAfter(SLTNode * pos); void SListDesTroy(SLTNode** pphead);在这里使用typedef重新定义int类型在之后对于链表的使用如果存储其他类型的数据的时候就可以直接在这里修改定义变量的时候就使用SLDatatype。使用struct结构体成员变量分为data以及SListNode*next。前者是在链表节点中存储的数据而后者则是用来链接节点之间的桥梁。在具体解释各个接口之前要解释的是如何在使用之前创建节点SLTNode* node1 (SLTNode*)malloc(sizeof(SLTNode)); node1-data 1; SLTNode* node2 (SLTNode*)malloc(sizeof(SLTNode)); node2-data 2; SLTNode* node3 (SLTNode*)malloc(sizeof(SLTNode)); node3-data 3; SLTNode* node4 (SLTNode*)malloc(sizeof(SLTNode)); node4-data 4;//建立了四个节点并储存从1到4四个数字 //将四个节点连接起来 node1-next node2; node2-next node3; node3-next node4; node4-next NULL;在这里首先定义SLTNode*类型的node1变量该指针指向的是malloc后的空间的地址然后解引用该地址将成员变量data进行赋值将数据存储在节点中然后将节点链接在一起将后一个节点的地址赋值给前一个节点的成员变量next。链表数据的打印void SLTPrint(SLTNode* phead) {SLTNode* pcur phead;while (pcur)//pcur!NULL{printf(%d-, pcur-data);pcur pcur-next;}printf(NULL\n);}该接口使用传地址修改的方式然后将传参地址重新赋给一个新的SLTNode*pcur来接收地址避免对原地址进行修改。尾节点的next指针最终指向NULL所以在这里的while循环结束条件就是当pcur最终指向空然后是对pcur循环赋值更新不断将下一节点的地址赋值给pcur。对于创建新的节点以及赋值接口SLTNode* SLTBuyNode(SLDataType x) {SLTNode* newnode (SLTNode*)malloc(sizeof(SLTNode));//因为malloc返回的是申请的空间的地址要使用指针来进行解引用if (newnode NULL) {perror(malloc fail!);exit(1);}newnode-data x;newnode-next NULL;return newnode;}在这里SLTNode*是返回的值newnode的类型在这里先通过malloc 来在堆上然后再进行if判断新节点是否为空如果为空则证明申请空间失败通过perror来打印出错误。如果是exit0证明程序正常结束然后返回0给操作系统。如果是非0则程序异常退出告知系统运行错误。然后将传递的参数x赋值给这个新节点然后将next赋值为NULL,最后再返回这个新的指针的地址节点的尾插//在这里进行二级指针的传参有两方面的考虑一方面是为了防止出函数后堆内存销毁所以在这里要进行传地址plist传递的是这个给变量本身的地址然后*plist得到的就是这个节点的地址。//然而在这SLTNode**pphead是为了保证类型匹配 SLTNode* ptail *pphead;在这里*pphead就是SLTNode*类型从而实现类型匹配进行赋值运算void SLTPushBack(SLTNode** pphead, SLDataType x) {assert(pphead);//增加断言不能为空无法解引用空指针SLTNode* newnode SLTBuyNode(x);//申请新的 节点//SLTNode* newnode(SLTNode*)malloc(sizeof(SLTNode));//在这里定义插入新的节点//在这里先找到尾结点再将尾节点和新节点连接在一起//空链表和非空链表if (*pphead NULL) {*pphead newnode;}else{SLTNode* ptail *pphead;while (ptail-next ! NULL) {//在这里的循环判断表达式节点中储存的下一个结点的地址不为空ptail ptail-next;}//patil指向的就是尾节点ptail-next newnode;}}在这里使用二级指针传参一方面考虑的是类型匹配问题另一方面则是为了保证传地址修改。使用断言保证传参地址不为空然后使用SLTBuyNode函数来申请新的节点。接下来进行判断传参地址表示的是空链表还是非空链表如果是空链表就使得新节点作为该链表的头节点如果为非空链表则使用while循环来找到尾节点然后将新节点插入到尾节点之后。头插节点接口void SLTPushFront(SLTNode** pphead, SLDataType x) {assert(pphead);SLTNode* newnode SLTBuyNodex;//newnode *ppheadnewnode-next *pphead;//将新节点与原本的第一个节点相连接*pphead newnode;//在使得该指针指向第一个节点 在这里*pphead相当于一个用来遍历每一个节点的指针在这里插入新的节点后要使得这个指针指向头节点}在这里传参使用二级指针的目的与尾插的目的相同另外直接将新节点插入链表开头即将新节点的成员变量next储存*pphead该节点的地址使得插入节点与原头节点相连。在这里*pphead始终是指向头节点的指针变量所以对其进行重新赋值使其指向新的头节点。删除节点接口void SLTPopBack(SLTNode** pphead) {assert(pphead *pphead);//链表不能为空//链表只有一个节点if ((*pphead)-next NULL)//-的优先级高于*{free(*pphead);*pphead NULL;}else {SLTNode* prev *pphead;SLTNode* ptail *pphead;while (ptail-next) {prev ptail;//在这里循环从前往后推移直到patil指向最后一个节点而prev为上一个节点ptail ptail-next;}//在这里prev为删除的那个节点的上一个节点中存储的地址因为最后一个节点被删除所以prev-next应该指向NULL//prev patil//释放free(ptail);ptail NULL;prev-next NULL;//在这里之所以要设置两个指针前后遍历因为在尾删这一块不仅仅是要free尾节点也要使得上一个节点的next为NULL,从而要得到尾节点的上一个节点的地址}}在这里断言链表不能为空且指向头节点的指针不能为空。然后使用if判断分支当链表只有一个节点的情况以及当链表不是只有一个节点的情况。当只有单个节点的时候直接将该节点free掉将堆上内存还回去。然后将指向头节点的指针置为空避免野指针的出现。当不是单个节点的情况下先定义两个SLTNode*类型的指针一个指向尾节点而另一个指向该尾节点的前一个节点具体操作逻辑是使用while循环终止条件是当ptail中的成员变量next为空。然后进行遍历循环从头节点开始。然后同样进行free将堆上内存释放再者将尾节点置为空将前一个节点的成员变量next置为空。头删接口void SLPopFront(SLTNode** pphead) {//既可以处理多节点的情况也可以处理单个节点的情况//链表不能为空assert(pphead *pphead);//在这里next只是一个临时的变量来存储指向第二个节点的地址SLTNode* next (*pphead)-next;//-优先级高于*free(*pphead);//在这里使得next中的地址转移给*pphead来作为头节点*pphead next;//使得*pphead指向第二个节点让第二个节点成为头节点}在这里为避免删除头节点之后而找不到新的链表的头节点的地址先定义一个指针来直线头节点的下一个节点然后将原头节点堆上空间释放。然后将指向头节点的指针指向新的头节点。节点查找接口SLTNode* SLTFind(SLTNode* phead, SLDataType x) {//在链表中查找节点中存储的数据SLTNode* pcur phead;//为了不修改phead这个指针因为在最后pcur最后会为空while (pcur)//等价于pcur!NULL;{if (pcur-data x) {return pcur;}pcur pcur-next;//在这里通过不断把下个节点的地址赋值给指针从而借助while循环实现遍历}//pcurNULL;在这里pcur可能最后为NULL,为此在这使用pcur指针代替指针phead;return 0;}//在这里**pphead是有可能值被改变的而pos是指拿来使用而不会被改变因为最后pcur会为空如果直接在原来指向头节点的指针上修改则会被修改所以在这里设置一个临时变量。使用while循环当节点中的成员变量正好等于数据x的时候就直接返回该节点的地址在循环过程中pcur指针是不断更新指向下一个节点的。当最终没有查找到对应的值的时候就直接结束。在指定的节点前插入新的节点的接口void SLTInsert(SLTNode** pphead,SLTNode *pos, SLDataType x) {assert(pphead *pphead);//在这里因为要找到对应的所要找到的节点的位置不为空assert(pos);SLTNode* newnode SLTBuyNode(x);//若pos*pphead;说明是头插if (pos *pphead) {SLTPushFront(pphead, x);}else{SLTNode* newnode SLTBuyNode(x);SLTNode* prev *pphead;while (prev-next ! pos) {prev prev-next;}//要让prev -newnode - pos 三者手牵手然后地址相连接newnode-next pos;prev-next newnode;}}同样在这里通过断言保证链表以及头节点指针不为空以及所查找的节点的地址不为空。然后使用if判断如果目标节点是头节点的情况下就直接调用头插函数如果不是头节点的情况下创建一个新的指针变量先储存头节点的地址避免对链表原头节点的地址进行修改。然后进行while循环来找到目标节点的前一个节点然后让该节点的成员变量next等于插入的新的节点的地址然后让该新节点的next保存目标节点的地址然后最后通过next串联起来使得三个节点实现了链接。在目标节点之后插入新的节点//在指定位置之后插入数据void SLTInsertAter(SLTNode* pos, SLDataType x)//为什么没有头节点{assert(pos);SLTNode* newnode SLTBuyNode(x);newnode-nextpos-next;pos-next newnode;}在这里先assert断言避免目标节点的地址为空然后创建一个新的节点使其成员变量next与目标节点的next相等表示为新的节点代替目标节点来指向下一个节点然后再将新的节点与目标节点相连pos-nextnewnode实现原目标节点与新节点的相连与目标节点的下一个节点的相连。删除节点操作接口void SLTErase(SLTNode** pphead, SLTNode* pos) {assert(pphead *pphead);//pos是头节点if (pos *pphead) {SLPopFront(pphead);}else {SLTNode* prev *pphead;while (prev-next ! pos) {prev prev-next;}prev-next pos-next;free(pos);pos NULL;}}在该接口中传参头节点地址然后分支进行判断如果目标节点等于头节点就直接进行头删操作else则将头节点地址赋值给一个新的指针变量避免堆原头节点的地址的修改。然后通过while循环来遍历到目标节点的前一个节点。然后先将目标价节点的下一个节点的地址赋值给目标节点的前一个节点的成员变量next目的是将目标节点删除后依旧可以保证链表的连续性然后再进行空间释放操作以及赋空操作删除目标节点之后的节点的接口void SLTEraseAfter(SLTNode* pos) {assert(pos pos-next);SLTNode* del pos-next;//pos del del-next就相当于pos-next-nextpos-next del-next;free(del);del NULL;}删除目标节点先将目标节点下一个节点的地址赋值给新的指针变量来避免数据的丢失然后再将目标前一个节点的成员变量next被赋值为目标节点的下一个节点的地址实现节点直接的相连。在保证目标节点前后的节点相连后才能对目标节点进行空间释放及赋空。销毁链表操作接口void SListDesTroy(SLTNode** pphead) {assert(pphead *pphead);SLTNode* pcur *pphead;while (pcur) {SLTNode* next pcur-next;free (pcur);pcur next;}//pcur*pphead NULL;//在这里使得头节点为空}在实现链表的销毁的操作过程中是节点逐个销毁的过程将后一个节点的地址赋值给新定义的相同类型的指针变量然后再对pcur这个指向节点的指针进行释放之后再将保留下一个节点的地址的next赋值给pcur来进行循环直到pcur指针为空则标志着销毁完成在进行free销毁后*pphead这个指向头节点的指针赋值为空避免野指针。结言对于单链表的解释和一些说法可能不够清楚犀利个人的见解和认识也是在数据结构的学习的开始时写的如果有错误和解释有误的地方欢迎指出
延伸阅读

更多相关文章

2026/10/27 8:00:03

pandas多维聚合生产实践:滚动窗口、宽表生成与业务可解释性

1. 项目概述:为什么多维聚合不是“加个groupby”就能搞定的事我在银行数据平台组干了八年,从最早用SQL写几十行嵌套子查询做客户分层,到后来带团队搭实时风险计算引擎,踩过的坑比写的代码还多。今天聊的这个主题——“多维聚合中的…

2026/10/27 9:55:04

道心通明:一个觉醒者的宇宙观

一、宇宙不可全部解构 宇宙的底层逻辑是千变万化的,谁也别想彻底解构它,这件事本身就不存在。我们能做的,不过是换一种看问题的方式,从一个别人没走过的窗口往外看。看到的东西自然不同,但这不等于“我看透了”。真正的…

2026/10/27 9:34:10

国际 Voice Agent 为什么 Demo 很惊艳,上线中文客服却不一定稳?

先说结论:国际 Voice Agent 的 Demo 往往能把“听懂—思考—说出来”这一小段实时交互做得很顺,所以第一次体验很容易惊艳。但中文客服上线面对的不是一段精心设计的对话,而是一条持续变化的业务链路:电话线路、噪声、身份确认、知…

2026/10/29 22:19:56

计算机视觉中的图形分类技术实践与优化

1. 图形分类学习概述图形分类是计算机视觉领域的基础任务之一,其核心目标是将输入的图像自动归类到预定义的类别中。这项技术在日常生活中有着广泛应用,从手机相册的自动分类到医疗影像诊断,再到工业质检系统,都离不开图形分类技术…

2026/10/29 22:19:56

Q学习强化人工蜂群算法在机器人路径规划中的应用

1. 项目概述:当人工蜂群遇上移动机器人2026年ASOC(自适应智能优化控制)领域最让我兴奋的突破,莫过于将Q学习驱动的人工蜂群算法应用于移动机器人多目标路径规划。这个组合乍看有些跨界,但实际测试中发现:经…

2026/10/29 22:19:56

图形分类技术解析:从传统方法到深度学习实践

1. 图形分类学习概述 图形分类学习是计算机视觉领域的基础任务之一,它通过算法自动识别和区分不同类别的图像。这项技术已经广泛应用于医疗影像分析、自动驾驶、工业质检等多个领域。简单来说,就是让计算机学会像人类一样"看懂"图片内容并进行…

2026/10/29 22:19:56

AI Agent高可用性设计与降级策略实践

1. AI Agent高可用性设计的核心挑战在复杂业务场景中,AI Agent的稳定性直接影响用户体验和业务连续性。去年某电商大促期间,我们团队部署的客服Agent因突发流量导致响应延迟飙升,最终触发了强制重启。这次事故让我深刻意识到:没有…

2026/10/29 22:19:56

马尾辫扎法全攻略:从颅顶高度到发际线修饰的实用造型技巧

1. 马尾这东西,真的被你低估了马尾辫(ponytail)大概是我这些年安利次数最多的一款发型了。不是因为它是多么高深的技术活,恰恰相反——它简单到谁都能上手,但它又是那种“看着容易、扎好很难”的存在。同样一个马尾&am…

2026/10/29 22:14:55

RTL8367S五口千兆交换机参考原理图拆解与避坑指南

简介:面向网络硬件工程师与嵌入式开发者的RTL8367S五口交换机参考原理图,聚焦芯片级硬件方案落地,帮助读者理解从芯片选型到电路实现的完整设计路径。压缩包内为1个PDF文件,约118KB,以单页原理图形式集中呈现MDI/MDIAN…

2026/10/29 17:05:52

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/29 17:06:02

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/29 17:06:13

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/29 0:03:42

学生选课系统DFD图绘制指南:边界划分与分层校验实战

简介:文档以顶层与第一层DFD图为核心,完整刻画学生选课系统的逻辑模型,面向软件工程课程设计、系统分析学习及毕设前期建模人群。顶层图界定系统边界,外部实体包括管理员、教师和学生三类角色;第一层图进一步细化用户登…

2026/10/29 0:03:42

JIRA敏捷落地:Scrum角色权限、Sprint与燃尽图避坑指南

简介:围绕“基于JIRA的敏捷开发项目管理”这一主题,面向产品负责人、Scrum Master及研发测试成员,梳理Scrum角色分工、迭代流程与JIRA工具配置的对应关系,适合初、中级团队导入敏捷管理时参考。压缩包共1个docx文件,大…

2026/10/29 0:03:42

SEMI S8人机工程验收实战:从条款拆解到现场实测的完整指南

简介:面向半导体设备设计、制造与运维工程师及安全合规人员,这份资料聚焦SEMI标准体系下的设备安全规范,重点收录SEMI S8-0308《半导体制造设备人体工程学工程安全指南》。内容围绕设备设计原则、错误与事故预防、人体工程学适配三大主线展开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑