【数据结构】单链表(二)

目录

1.查找数据

2.指定位置插入和删除节点

2.1 指定位置之前插入节点

2.2 指定位置之后插入节点

2.3 删除指定位置节点

2.4 删除指定位置之后的节点

3.销毁链表


我们接着上一篇【数据结构】单链表(一)-CSDN博客 来继续实现单链表

1.查找数据

SList.h中进行函数的声明

SLNode* SLfind(SLNode* pps, Type x);//查找

返回值是一个地址,如果找到,就返回这个数的地址,如果没找到,就返回NULL,参数就是链表首节点地址和要查找的数

SList.c中进行函数的实现

首先我们可以再定义一个指针存放首节点地址,这样的话在后面的遍历链表时就不会改变pps的指向了

SLNode* SLfind(SLNode* pps, Type x)//查找
{SLNode* pcur = pps;//新定义一个指针,指向首节点
}

然后就是循环遍历

SLNode* SLfind(SLNode* pps, Type x)//查找
{SLNode* pcur = pps;//新定义一个指针,指向首节点while (pcur)//pcur不能为空{if (pcur->data == x) //找到return pcur;//直接返回地址pcur = pcur->next;//没找到往后找}
}

当跳出while循环时,证明没找到,此时pcur为空,我们直接返回NULL

SLNode* SLfind(SLNode* pps, Type x)//查找
{SLNode* pcur = pps;//新定义一个指针,指向首节点while (pcur)//pcur不能为空{if (pcur->data == x) //找到return pcur;//直接返回地址pcur = pcur->next;//没找到往后找}return NULL;//没找到
}

test.c中测试一下

void SListtest3()
{SLNode* plist = NULL;//空链表SLPushBack(&plist, 1);//尾插SLPushBack(&plist, 2);SLPushHead(&plist, 6);//头插SLPushHead(&plist, 7);SLPrint(plist);//打印SLNode* find = SLfind(plist, 2);if (find == NULL)printf("没找到\n");elseprintf("找到了\n");
}
int main()
{//SListtest1();//SListtest2();SListtest3();return 0;
}

 自己测试的时候可以多测几次

2.指定位置插入和删除节点

上一篇我们说了头部尾部的插入和删除数据,现在我们来实现一下指定位置的插入和删除数据

2.1 指定位置之前插入节点

SList.h中进行函数的声明

void SLInsert(SLNode** pps, SLNode* pos, Type x);//指定之前插

参数有三个:链表首节点的地址,指定的位置,要插入的数据

SList.c中进行函数的实现

 现在我们要在节点3前面插入一个节点,就要让节点2里面的next指向新节点,新节点里面的next指向节点3

我们先找pos的前一个结点 ,用循环遍历

void SLInsert(SLNode** pps, SLNode* pos, Type x)//指定之前插
{assert(pps && *pps);assert(pos);SLNode* prev = *pps;//再定义一个指针变量初始指向首节点while (prev->next != pos){prev = prev->next;}
}

跳出循环后此时prev指向pos前一个节点,然后让这些节点“手牵手”

void SLInsert(SLNode** pps, SLNode* pos, Type x)//指定之前插
{assert(pps && *pps);assert(pos);SLNode* newnode = SLBuyNode(x);//插入的数据SLNode* prev = *pps;//再定义一个指针变量初始指向首节点while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;
}

代码写到这里我们在分析一下pos为1时可不可行

 这种情况下prev会一直往后走,直到走到最后一个节点,上面的代码在次情况下行不通

我们再分析一下pos为最后一个节点时可不可行

依旧是让节点3里面的next指向新节点,新节点里面的next指向节点4

经分析,上面的代码在这种情况下可行,所以不可行的就是pos为1的情况,我们单独把这种情况列出来,其实pos为1时也就是头插的情况

void SLInsert(SLNode** pps, SLNode* pos, Type x)//指定之前插
{assert(pps && *pps);assert(pos);SLNode* newnode = SLBuyNode(x);//插入的数据if (pos == *pps){SLPushHead(pps, x);//直接调用头插代码}else//其他位置{SLNode* prev = *pps;//再定义一个指针变量初始指向首节点while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;}
}

这就是完整的代码

test.c中测试一下

void SListtest3()
{SLNode* plist = NULL;//空链表SLPushBack(&plist, 1);//尾插SLPushBack(&plist, 2);SLPushHead(&plist, 6);//头插SLPushHead(&plist, 7);SLPrint(plist);//打印SLNode* find = SLfind(plist, 2);//找2SLInsert(&plist, find, 11);//直接插在2前面SLPrint(plist);//打印
}
int main()
{SListtest3();return 0;
}

看结果

其他情况有疑惑的话一定要自己测试运行一下

2.2 指定位置之后插入节点

SList.h中进行函数的声明

void SLAfter(SLNode* pos, Type x);//指定之后插

这里只有两个参数,一个是指定位置,一个是要插入的值,这里我们不需要知道头节点,因为可以通过pos找到下一个节点,在指定位置之前插入数据的函数需要头节点是因为我们不能通过pos找到pos的前一个节点

SList.c中进行函数的实现

void SLAfter(SLNode* pos, Type x)//指定之后插
{assert(pos);SLNode* newnode = SLBuyNode(x);//插入的数据newnode->next = pos->next;pos->next = newnode;
}

注意:  newnode->next = pos->next;   pos->next = newnode;这两句代码的顺序不可以交换,交换后是错的

test.c中测试一下

void SListtest3()
{SLNode* plist = NULL;//空链表SLPushBack(&plist, 1);//尾插SLPushBack(&plist, 2);SLPushHead(&plist, 6);//头插SLPushHead(&plist, 7);SLPrint(plist);//打印SLNode* find = SLfind(plist, 2);//找2SLInsert(&plist, find, 11);//直接插在2前面SLPrint(plist);//打印SLAfter(find, 5);//插在2后面SLPrint(plist);//打印
}
int main()
{SListtest3();return 0;
}

代码没有问题

2.3 删除指定位置节点

SList.h中进行函数的声明

void SLErase(SLNode** pps, SLNode* pos);//删除pos节点

参数是二级指针,接收首节点地址,还有一个参数是要删除的节点

SList.c中进行函数的实现

我们要让pos的前一个节点指向pos的后一个节点,然后把pos这个节点销毁

既然要找pos的前一个节点,我们依旧是定义一个指针prev,初始为*pps,往后一个一个找,直到找到pos前一个节点 

void SLErase(SLNode** pps, SLNode* pos)//删除pos节点
{assert(pps && *pps);assert(pos);SLNode* prev = *pps;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;
}

如果此时链表只有一个节点,上面的代码可行吗?来分析一下

发现代码走不通,其实这种情况就是头删的情况,我们直接调用头删的代码就可以了

void SLErase(SLNode** pps, SLNode* pos)//删除pos节点
{assert(pps && *pps);assert(pos);if (pos == *pps)//一个节点{SLPopHead(pps);}else//多个节点{SLNode* prev = *pps;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}

test.c中测试一下

void SListtest3()
{SLNode* plist = NULL;//空链表SLPushBack(&plist, 1);//尾插SLPushBack(&plist, 2);SLPushHead(&plist, 6);//头插SLPushHead(&plist, 7);SLPrint(plist);//打印SLNode* find = SLfind(plist, 7);//找7SLErase(&plist, find);//删除指定位置节点SLPrint(plist);//打印
}
int main()
{SListtest3();return 0;
}

删除成功

2.4 删除指定位置之后的节点

SList.h中进行函数的声明

void SLPushAfter(SLNode* pos);//删除pos之后的节点

pos的后一个节点我们可以直接通过pos找到,就不需要头节点地址,所以一个参数就好了

 在SList.c中进行函数的实现

 

还是先让pos这个节点找到它的下下个节点,然后再销毁pos后面的节点

 这里呢我们需要一个临时变量存放pos->next的地址,然后再连接节点

 我们先写一下代码,让pos和pos下下个节点相连

void SLPushAfter(SLNode* pos)//删除pos之后的节点
{assert(pos);assert(pos->next);SLNode* temp = pos->next;pos->next = temp->next;
}

 然后销毁pos下一个节点并置空

void SLPushAfter(SLNode* pos)//删除pos之后的节点
{assert(pos);assert(pos->next);SLNode* temp = pos->next;//临时变量pos->next = temp->next;//连接free(temp);//销毁temp = NULL;//置空
}

test.c中测试一下

void SListtest3()
{SLNode* plist = NULL;//空链表SLPushBack(&plist, 1);//尾插SLPushBack(&plist, 2);SLPushHead(&plist, 6);//头插SLPushHead(&plist, 7);SLPrint(plist);//打印SLNode* find = SLfind(plist, 7);//找7SLPushAfter(find);//删除指定位置后一个节点SLPrint(plist);//打印
}
int main()
{SListtest3();return 0;
}

3.销毁链表

跟顺序表一样,链表使用完之后也要销毁,链表由一个一个节点组成,所以也要一个一个销毁

SList.h中进行函数的声明

void SLDestroy(SLNode** pps);//销毁

参数就是首节点地址

SList.c中进行函数的实现

我们在销毁当前节点之前要把下一个节点的信息存起来

 销毁空间

pcur后移到提前保存的next处

 

然后next后移,把当前的pcur销毁

就这样一直往后,直到pcur为空

代码来实现一下


void SLDestroy(SLNode** pps)//销毁
{assert(*pps && pps);SLNode* pcur = *pps;while (pcur){SLNode* next = pcur->next;//存下节点信息free(pcur);//释放pcur = next;//往后走}*pps = NULL;//不要忘了头节点此时没有置空,要置空
}

test.c中测试一下

void SListtest3()
{SLNode* plist = NULL;//空链表SLPushBack(&plist, 1);//尾插SLPushBack(&plist, 2);SLPushHead(&plist, 6);//头插SLPushHead(&plist, 7);SLPrint(plist);//打印SLDestroy(&plist);//销毁SLPrint(plist);//打印
}
int main()
{SListtest3();return 0;
}

可以自己通过调试看结果,能看到更详细,打印出来看也可以

单链表实现就分享到这里,拜拜~ 

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

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

相关文章

前端开发学习笔记 3 (Chrome浏览器调试工具、Emmet语法、CSS复合选择器、CSS元素选择模式、CSS背景)

文章目录 Chrome浏览器调试工具Emmet语法CSS复合选择器后代选择器子选择器并集选择器伪类选择器 CSS元素选择模式元素选择模式概述CSS块标签CSS行内标签CSS行内块标签CSS元素显示模式转换 CSS背景CSS背景颜色CSS背景图片CSS背景图片平铺CSS背景图片位置CSS背景图片固定CSS背景复…

SpringAI初体验之HelloWorld

目录 前言1.准备工作2.初始化项目3.解决问题3.1 Connection Time out 连接超时问题3.2 You exceeded your current quota 额度超限问题 4.访问调用5.总结 前言 在逛SpringBoot页面时突然看到页面上新增了一个SpringAI项目,于是试了一下,感觉还行。其实就是封装了各家…

Redis性能管理和集群的三种模式(二)

一、Redis集群模式 1.1 redis的定义 redis 集群 是一个提供高性能、高可用、数据分片、故障转移特性的分布式数据解决方案 1.2 redis的功能 数据分片:redis cluster 实现了数据自动分片,每个节点都会保存一份数据故障转移:若个某个节点发生故…

网络协议学习——以太网协议

目录 ​编辑 一,以太网简介 二,以太网通信的过程 为什么不用IP地址? 过程 MAC帧 MAC帧的字段介绍 ARP协议 传输过程的一些问题 RARP协议 提高效率 三,其他问题 ARP诈骗问题 URL解析过程 一,以太网简介 …

AI大模型基石:文字与数字的起源与演变

AI大模型基石:文字与数字的起源与演变 1、文字 1.1、起源 我们的祖先在还没有发明文字和语言之前就已经开始使用“咿咿呀呀”的声音来传播信息了,比如在野外活动遇到危险,然后发出“咿咿呀呀”的声音来提醒同伴小心,同伴在接收到…

GEE数据集——1986年—2022年加拿大全国烧毁面积综合数据 (NBAC)

简介 加拿大全国烧毁面积综合数据 (NBAC) 全国烧毁面积综合数据 (NBAC) 是一个地理信息系统数据库和系统,用于计算自 1986 年以来每年全国范围内烧毁的森林面积。这些数据用于帮助估算加拿大的碳排放量。烧毁面积是通过评估一系列可用数据源确定的,这些…

废品回收小程序推动回收行业的发展趋势

回收在全球都是一个重要行业,它为全球的环保作出了重要贡献。 随着科技的不断发展创新,废品回收的方式也逐渐多样,全新的线上回收小程序也逐渐出现在大众的生活中,在当下的手机时代,线上回收也为大众提供了更加便利的…

vs2022启动cmake项目(qt+c++)

1.本工程,如图,1个cmakelist.txt3个文件 2.启动vs 3.选择文件夹 4.进入这个页面,就说明配置没问题 5.启动 6.最后会自己生成其他文件

本地MinIO存储服务通过Java程序结合cpolar实现远程连接上传文件

文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 前言 MinIO是一款高性能、分布式的对象存储系统,它可以100%的运行在标准硬件上,即X86等…

idea 卡怎么办

设置内存大小 清缓存重启 idea显示内存全用情况 右下角

适配器模式类图与代码

某软件系统中,已设计并实现了用于显示地址信息的类Address,现要求提供基于Dutch语言的地址信息显示接口。为了实现该要求并考虑到以后可能还会出现新的语言的接口,决定采用适配器(Adapter)模式实现该要求,得到如图7.9所示的类图。 【Java代码…

Docker操作容器打包(commit),压缩(save),挂载(load)

文章目录 前言一、容器打包二、将镜像压缩成tar包三、将tar包挂载为镜像结束 前言 将容器打包成镜像时,你正在将应用程序及其所有依赖项、文件和配置文件捆绑到一个可移植的、独立的单元中。这样做可以确保您的应用程序在不同环境中具有一致的运行方式,…

ASUS华硕ROG幻16Air笔记本电脑GU605M原装出厂Win11系统工厂包下载,带有ASUSRecovery一键重置还原

适用型号:GU605MI、GU605MY、GU605MZ、GU605MV、GU605MU 链接:https://pan.baidu.com/s/1YBmZZbTKpIu883jYCS9KfA?pwd9jd4 提取码:9jd4 华硕原厂Windows11系统带有ASUS RECOVERY恢复功能、自带所有驱动、出厂主题壁纸、系统属性联机支持…

Linux磁盘空间问题排查记录

问题 pip install时总提示OSError(28, ‘No space left on device’)或者ERROR: Could not install packages due to an OSError: [Errno 28] No space left on device 分析 很明显,磁盘空间不足。尝试了以下方法,没有解决问题: 清理pip缓…

【论文阅读笔记】Attention Is All You Need

论文小结 这是17年的老论文了,Transformer的出处,刚发布时的应用场景是文字翻译。BLUE是机器翻译任务中常用的一个衡量标准。 在此论文之前,序列翻译的主导模型是RNN或者使用编解码器结构的CNN。本文提出的Transformer结构不需要使用循环和卷…

左总视角:千视以NDI 6重塑实时流媒体传输格局

欧洲当地时间4月3日下午1点,NDI 官方宣布了NDI 6.0版本的正式上线。凭借原生HDR和10比特/12比特色彩支持,NDI 6将NDI源的画质处理推向了一个新的巅峰,成为了高画质行业内容创作者的首选。此外,跨互联网现在也可以通过内嵌到SDK组件…

sysbench MySQL性能测试

目录 1. QPS&&TPS 1.1 数据库启动到现在的运行时间(秒) 1.2 查询量 1.3 status命令直接显示出QPS 1.4 每秒输出数据库状态(累加) 2. sysbench 测试工具 3. OLTP MySQL测试 3.1 普通参数 3.2 支持的lua脚本 3.3 脚本参数 3.4 测试数据准备 3.5 进行测试 3.…

蓝桥杯-数组切分

问题描述 已知一个长度为 N 的数组: A1,A2,A3,...AN 恰好是1~ N的一个排列。现 在要求你将 4 数组切分成若干个 (最少一个,最多 N 个)连续的子数组,并且 每个子数组中包含的整数恰好可以组成一段连续的自然数。 例如对于 4 1,3,2,4,一共有 5 种切分方法: 1324:每个单独的数显然…

Java 中文官方教程 2022 版(四十六)

原文&#xff1a;docs.oracle.com/javase/tutorial/reallybigindex.html 定义简单的通用类型 原文&#xff1a;docs.oracle.com/javase/tutorial/extra/generics/simple.html 这里是包java.util中接口List和Iterator的定义的一个小节选&#xff1a; public interface List <…

盲人独立购物新纪元:一款实时“障碍物识别”应用助力超市之行

作为一名资深记者&#xff0c;我始终热衷于探寻科技如何助力特殊群体跨越生活挑战的创新实践。近日&#xff0c;一款名为蝙蝠避障专为盲人设计的辅助应用走进了我的视野&#xff0c;它凭借实时障碍物识别功能&#xff0c;助力视障人士独立前往超市购物&#xff0c;悄然改变了他…