92.【C语言】数据结构之单向链表的查找,中间插入和删除,销毁
目录
1.链表的查找函数
2.链表的修改函数
3.链表的中间插入函数
1.在pos之前插入:SLTInsertBefore函数
1.借助头指针pphead
示意图
代码示例(写入SList.c)
头文件添加SLTInsertbefore的声明
main.c的部分代码改为
1.测试中间插入
2.测试头部插入
3.测试pos为NULL的情况
2.不借助头指针pphead
代码示例
2.在pos之后插入:SLTInsertAfter函数
代码示例
错误写法
4.链表的中间删除函数
示意图(非头删)
1.在pos之前删除:SLTEraseBefore函数
1.借助头指针pphead
代码示例
头文件添加SLTErase的声明
main.c的部分代码改为
运行结果
2.不借助头指针pphead
示意图
代码示例
2.在pos之后删除:SLTEraseBeforeAfter函数
代码示例
4.链表的销毁函数
代码示例(写入SList.c)
main.c部分代码改为
运行结果
错误写法
VS2022+debug+x86环境下调试错误代码
承接91.【C语言】数据结构之链表的头删和尾删文章
这里均以单向链表做演示
1.链表的查找函数
代码示例(遍历查找)
void SLTFind(SLTNode* phead, SLTDataType find)
{SLTNode* cur = phead;//初始化cur指针while (cur){if (cur->data == find){return cur;}//找不到则将下一个节点的地址赋值给curcur = cur->next;}//从头找到尾没找到,则返回NULLreturn NULL;
}
2.链表的修改函数
有了查找函数,就可以轻松实现修改函数
修改函数的策略:先查某个节点的数据,再对这个数据进行修改
代码示例(写入SList.c)
void SLTMod(SLTNode* phead, SLTDataType find, SLTDataType mod)
{//将需要修改数据的节点的地址赋值给tar_p//tar_p为目标指针(target_pointer)SLTNode* tar_p = SLTFind(phead, find);//修改目标节点的数据tar_p->data = mod;
}
main.c部分代码改为
void TestSList1()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTFind(plist, 3);SLTMod(plist, 3, 5);SLTPrint(plist);
}
执行结果
3.链表的中间插入函数
插入包括:在pos之前插入和在pos之后插入
1.在pos之前插入:SLTInsertBefore函数
1.借助头指针pphead
示意图
在pos之前插入,要找pos前的节点,需要参数SLTNode* phead
代码示例(写入SList.c)
void SLTInsertBefore(SLTNode** pphead, SLTNode* pos,SLTDataType x)
{//判断pos是否为NULLassert(pos);//pphead不能为NULLassert(pphead);//判断pos是否为头指针,如果是则调用现有的函数if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//初始化新节点SLTNode* newnode = BuySLTNode(x);prev->next = newnode;newnode->next = pos;}
}
注意:
1.一开始就要判断判断pos是否为NULL
2.将pos分是否为头指针两种情况讨论
如果不判断pos是否为头指针,直接硬插入的话,会报错
分析原因:当pos为头指针时,pos和prev指针存储的内容是一样的,第一次循环条件prev->next!=pos成立,错过了prev->next==pos的机会,则while会一直循环,直到prev为NULL(x86下存储的00 00 00 00),出现NULL->next是不合法的,因此报错
3.pphead不能为NULL(即plist存储的地址不能为NULL),plist可以为NULL(空链表)
头文件添加SLTInsertbefore的声明
main.c的部分代码改为
1.测试中间插入
void TestSList1()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTNode* pos = SLTFind(plist, 3);SLTInsertBefore(&plist, pos, 5);SLTPrint(plist);
}
2.测试头部插入
SLTNode* pos = SLTFind(plist, 1);
3.测试pos为NULL的情况
均可以实现
2.不借助头指针pphead
只需要交换即可
代码示例
void SLTInsertBefore(SLTNode** pos, SLTDataType x)
{SLTNode* newnode = BuySLTNode(x);//用中间变量n_tmp(全称next_tmp)交换SLTDataType tmp = (*pos)->data;(*pos)->data = newnode->data;newnode->data = tmp;//找pos的下一个节点,暂存到n_tmpSLTNode* n_tmp = (*pos)->next;//变动指针(*pos)->next = newnode;newnode->next = n_tmp;
}
头插和尾插均可
2.在pos之后插入:SLTInsertAfter函数
代码示例
void SLTInsertAfter(SLTNode* pos,SLTDataType x)
{assert(pos);//pos不可为NULLSLTNode* newnode = BuySLTNode(x);newnode->next = pos->next;pos->next = newnode;
}
错误写法
void SLTInsertAfter(SLTNode* pos,SLTDataType x)
{assert(pos);//pos不可为NULLSLTNode* newnode = BuySLTNode(x);pos->next = newnode;newnode->next = pos->next;
}
反过来写会出问题,比如想在d2节点后插入新节点
这样做是无效的
4.链表的中间删除函数
示意图(非头删)
1.在pos之前删除:SLTEraseBefore函数
1.借助头指针pphead
代码示例
void SLTEraseBefore(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(pos);assert(*pphead);if (*pphead == pos){SLTPopFront(pphead);}else{//找pos的前一个SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);}
}
注意:
1.断言pphead,*pphead(即plist)和pos
2.头删直接调用SLTPopFront函数
3.不采取pos = NULL;,SLTErase中的pos为形参,没有办法真正为删除的节点含有的指针置NULL,应该在main.c中操作
头文件添加SLTErase的声明
main.c的部分代码改为
void TestSList1()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTNode* pos = SLTFind(plist, 3);SLTEraseBefore(&plist,pos);pos = NULL;SLTPrint(plist);
}
注意pos = NULL;
运行结果
2.不借助头指针pphead
示意图
代码示例
void SLTEraseBefore(SLTNode** pos)
{assert((*pos)->next);(*pos)->data = ((*pos)->next)->data;(*pos)->next = ((*pos)->next)->next;
}
//或写成下面这样
void SLTEraseBefore(SLTNode* pos)
{assert(pos->next);pos->data = (pos->next)->data;pos->next = (pos->next)->next;
}
注意:此方法有缺陷,不能尾删,因此断言
2.在pos之后删除:SLTEraseBeforeAfter函数
代码示例
void SLTEraseAfter(SLTNode* pos)
{assert(pos);//不为空链表assert(pos->next);//不为末尾节点SLTNode* delete = pos->next;//也可以写成pos->next = delete->next;pos->next = (pos->next)->next;free(delete);delete = NULL;
}
注意:不可以不写SLTNode* delete = pos->next;,一旦pos->next被修改后,要删除的节点的指针会丢失,因此要先用结构体指针delete保存
4.链表的销毁函数
代码示例(写入SList.c)
采用双指针法
void SLTDestory(SLTNode* phead)
{SLTNode* p1 = phead;while (p1){SLTNode* p2 = p1->next;free(p1);p1 = p2;}
}
注:
1.p1相当于慢指针,p2相当于快指针
2.一定先保存p1->next才能free(p1);防止p1->next值被操作系统修改从而找不到下一个节点的地址!
main.c部分代码改为
void TestSList1()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);SLTDestory(plist);plist = NULL;//不能在SLTDestory内置NULL,形参的改变不影响实参
}
注:如果不想在main.c中手动为plist置空,可以修改SLTDestory(&plist);
void SLTDestory(SLTNode** pphead)
{assert(pphead);SLTNode* p1 = *pphead;while (p1){SLTNode* p2 = p1->next;free(p1);p1 = p2;}*pphead = NULL;
}
运行结果
错误写法
void SLTDestory(SLTNode* phead)
{SLTNode* p1 = phead;while (p1){SLTNode* p2 = p1;free(p1);p1 = p2->next;}
}
对free的本质认识不清楚,free函数是将指针所指向的内存空间的使用权交还给操作系统,操作系统会改变内存空间的值,并没有改变指针的值,因此p2->next会变成野指针
VS2022+debug+x86环境下调试错误代码
SLTNode* p2 = p1;执行后
free(p1);执行后
p1->next被操作系统修改成0xdddddddd,p1->next为野指针
第二次执行完SLTNode* p2 = p1;后
显然再次执行free(p1);肯定会报错,访问冲突,无权限修改内存
相关文章:

92.【C语言】数据结构之单向链表的查找,中间插入和删除,销毁
目录 1.链表的查找函数 2.链表的修改函数 3.链表的中间插入函数 1.在pos之前插入:SLTInsertBefore函数 1.借助头指针pphead 示意图 代码示例(写入SList.c) 头文件添加SLTInsertbefore的声明 main.c的部分代码改为 1.测试中间插入 2.测试头部插入 3.测试pos为NULL的…...

WPF+MVVM案例实战(七)- 系统初始化界面字体描边效果实现
文章目录 1、案例效果展示2、项目准备3、功能实现1、资源获取2、界面代码3、后台代码4 源代码获取1、案例效果展示 2、项目准备 打开项目 Wpf_Examples,新建系统初始化界面 WelcomeWindow.xmal,如下所示: 3、功能实现 1、资源获取 案例中使用的CSDN文字为路径文字,从字体…...
基于 C# 的 AI 算法测试方法
基于 C# 的 AI 算法测试方法 在当今人工智能蓬勃发展的时代,AI 算法的质量和可靠性至关重要。对于使用 C# 开发的 AI 算法,我们需要一套有效的测试方法来确保其性能、准确性和稳定性。本文将详细探讨基于 C# 的 AI 算法测试方法,帮助开发者更…...

Find My画框|苹果Find My技术与画框结合,智能防丢,全球定位
画框通常用于保护和固定艺术品,尤其是绘画作品。它是一种可以展示艺术品的框架,用于保护艺术品免受损坏或污染。艺术品被放置在画框内,可以避免受到空气、尘土和其他外部因素的损害。同时,画框还可以增强艺术品的展示效果…...

布谷语音源码服务器搭建环境及配置流程
布谷语音源码部署环境安装要求(只有在相同的环境下才更容易避免一些不必要的麻烦):●安装Center OS 7.9,我们自己的服务器使用的是7.9建议相同系统,非强制●安装宝塔环境(强烈推荐使用)●安装软…...

算法|牛客网华为机试21-30C++
牛客网华为机试 上篇:算法|牛客网华为机试10-20C 文章目录 HJ21 简单密码HJ22 汽水瓶HJ23 删除字符串中出现次数最少的字符HJ24 合唱队HJ25 数据分类处理HJ26 字符串排序HJ27 查找兄弟单词HJ28 素数伴侣HJ29 字符串加解密HJ30 字符串合并处理 HJ21 简单密码 题目描…...

Tomcat servlet response关于中文乱码的经验
前言 最近修改老项目项目,使用zuul网关返回的中文内容乱码了,如果使用GBK或者GB2312编码确正常显示,稍微实验了一下,发现里面很多细节,毕竟Springboot对我们做了很多事情,而且当我们使用不同的模式会出现很…...

WebGIS开发丨从入门到进阶,全系列课程分享
WebGIS开发所需的技能 1.前端技能:Html、CSS、 Javascript、WebAPLs、Vue 2.二维技能:WebGIS基础理论及开发、MapGIS二次开发Openlayers、Leaflet、Mapbox 、Echarts、公共开发平台开发等 3.三维技能:Blender、Three.js、Cesium等 Web开发…...
C++ 模板专题 - 标签分派(Tag Dispatching)
一:概述: 在 C 中,Tag Dispatching 是一种编程技巧,主要用于在编译期根据不同的类型或特征选择不同的函数重载或代码分支。Tag Dispatching 借助类型标签(tags)进行函数调度,用于在模板中实现编译期的静态分…...

如何解决RabbitMQ消息的重复消费问题
什么情况下会导致消息的重复消费——在消费者还没成功发送自动确认机制时发生: 网络抖动消费者挂了 解决方案 每条消息设置一个唯一的标识id幂等方案:【Redis分布式锁、数据库锁(悲观锁、乐观锁)】 面试官:如何解决…...

Java调用chatgpt
目前openai的chatgpt在国内使用有一定难度,不过国内的大模型在大部分情况下已经不弱于chatgpt,而且还更便宜,又能解决国内最敏感的内容安全问题。本文后续以spring ai调用国内chatgpt厂商实现为例,讲解怎么构建一个java调用chatgp…...

将你的 Kibana Dev Console 请求导出到 Python 和 JavaScript 代码
作者:来自 Elastic Miguel Grinberg Kibana Dev Console 现在提供将请求导出到可立即集成到你的应用程序中的 Python 和 JavaScript 代码的选项。 你使用过 Kibana 开发控制台吗?这是一个非常棒的原型设计工具,可让你以交互方式构建和测试 El…...

成都世运会志愿者招募报名流程及证件照制作方法
成都世运会志愿者招募正在如火如荼地进行中,许多热心公益的青年们纷纷报名参与。本文将详细介绍如何通过官方渠道报名,并使用手机来自行制作符合要求的4:5比例的白底证件照。 一、志愿者报名流程概述首先,报名成都世运会志愿者需要通过官方指…...

大数据技术的前景如何?
在当今数字化迅猛发展的时代,大数据技术的前景显得尤为广阔。随着数据量的激增,如何有效利用这些数据成为了各行各业关注的焦点。未来五年,大数据技术的发展趋势可以从市场规模、技术融合、行业应用和政策支持等多个方面进行深入分析。 1. 市…...

LLM | 论文精读 | 基于大型语言模型的自主代理综述
论文标题:A Survey on Large Language Model based Autonomous Agents 作者:Lei Wang, Chen Ma, Xueyang Feng, 等 期刊:Frontiers of Computer Science, 2024 DOI:10.1007/s11704-024-40231-1 一、引言 自主代理(…...

详解Redis相关缓存问题
目录 缓存更新策略 定期⽣成 实时⽣成 缓存淘汰策略 Redis内置缓存淘汰策略 缓存预热 缓存穿透 缓存雪崩 缓存击穿 缓存更新策略 定期⽣成 每隔⼀定的周期(⽐如⼀天/⼀周/⼀个⽉), 对于访问的数据频次进⾏统计. 挑选出访问频次最⾼的前 %N的数据. 实时⽣成 先给缓存…...

ubuntu 24 (wayland)如何实现无显示器远程桌面
ubuntu 24默认采用的是wayland而非x11,查过文档vnc对wayland的支持不是很好,折腾了好久,弄了一个如下的方案供参考: 硬件条件 需要一个显卡欺骗器或者可以接HDMI口作为视频信号源输出的设备。 将ubuntu的主机的HDMI输出接到该硬…...

《模拟电子技术基础》第六版PDF课后题答案详解
《模拟电子技术基础》第六版是在获首届全国优秀教材建设奖一等奖的第五版的基础上,总结6年来的教学实践经验修订而成的新形态教材。为满足国家人才培养的需求,适应新型教学模式,并考虑到大多数院校逐渐减少课程学时的现状,在不降低…...
python知识收集
文章目录 语法def声明函数class声明类class 子类(父类) 继承 数据结构列表列表操作 元组元组操作 字典遍历字典 集合 文件读写读文件写文件 csv模块读入写入 time模块发送邮件制作二维码滚动广告 语法 def声明函数 class声明类 class 子类(父类) 继承 数据结构 列表 列表…...
传奇996_3——使用补丁添加怪物
找素材,看素材是否是打包好的,没有的话就使用工具进行素材打包(打包好后尽量别改名),并复制进客户端,D:\chuanqinew\996M2_debug\dev\anim\monster找到模型表cfg_model_info.xls,复制表中前几行…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...