链表详解(三)
目录
- 链表功能实现
- 链表的查找SLNode* SLFind(SLNode* phead, SLNDataType x)
- 代码
- 链表任意位置前插入void SLInsert(SLNode**pphead,SLNode* pos, SLNDataType x)
- 代码
- 链表任意位置前删除void SLErase(SLNode**pphead,SLNode* pos)
- 代码
- 链表任意位置后插入void SLInsertAfter( SLNode* pos,SLNDataType x)
- 代码
- 例题
- 链表任意位置后删除void SLEraseAfter(SLNode* pos, SLNDataType x)
- 代码
- 销毁链表void SLDestroy(SLNode** pphead)
- 代码
链表功能实现
链表的查找SLNode* SLFind(SLNode* phead, SLNDataType x)
我们用指针cur去遍历这个链表,如果cur的数据val值是和x想等的,那么就直接返回cur这个位置的节点,如果cur->val!=x,那么我们就让cur走到下一个节点cur=cur->next,当遍历完整个链表后我们还是没有找到和x相等的val,那么我们就直接返回一个NULL就行了
代码
SLNode* SLFind(SLNode* phead, SLNDataType x)
{assert(pphead);SLNode* cur = phead;while (cur){if (cur->val == x){return cur;}else{cur = cur->next;}}return NULL;
}
链表任意位置前插入void SLInsert(SLNode**pphead,SLNode* pos, SLNDataType x)
函数功能为在pos位置前插入链表数据x
在实现这个函数之前,有一个问题,就是pphead和*pphead的区别
pphead是一个二级指针,所有pphead表示对所一级指针的地址,也就是指向链表头节点指针的地址
*pphead是对pphead解引用,表示指向链表头节点的指针,注意不是指向链表头节点指针的地址
那么我们知道了这两个都区别,我们再来看看下面这个问题
pphead *pphead pos这三个是否需要断言
首先pphead断言 assert(pphead)表示我们要检查指向链表头节点指针的地址是否为空,换句话说就是如果传入为空,这个函数是否会出现问题,显然,如果我们传入为空,那么我们就无法找到指向链表头节点指针,
那*pphead断言assert(*pphead)就表示我们要检查链表头节点指针是否指向的空,如果指向空,就代表这个链表为空链表,我们就可以当这个函数为头插,或者尾插,所有这并不会对这个函数造成影响
pos断言assert(pos)表示传入参数插入位置指针的地址是否为空,如果为空,那么我们也就找不到插入的位置,所以pos是需要断言的
但是有一种情况pos只能为空,就是*pphead为空,空链表我们要插入的地址当然只能传空,为了防止这种情况我们就需要像这样断言 assert((!(*pphead) == !pos) || pos && *pphead)
!(*pphead) == !pos)表示pos为空时为假,pphead为空时也为假,通过!对两个取反,使假变成真
pos && pphead表示pos和pphead都不为空
这两个只需要满足其中的一个就可以继续用插入函数
当pos和pphead二者中只有一个为空时就会断言报错
还有一种情况就是pos的位置根本不在链表中,我们整个链表都找完了,就是没有找到pos的位置,所有我们要判断当链表遍历完时,仍然没有找到pos的位置,我们就需要提醒一下找不到pos的位置
代码
void SLInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{assert(pphead);assert((!(*pphead) == !pos) || pos && *pphead);if ((*pphead == pos))//头插情况{SLPushFront(pphead, x);}else {SLNode* prev = *pphead;while (prev->next != pos){if (prev->next == NULL){printf("找不到pos的位置");exit(-1);}prev = prev->next;} SLNode* newnode = CreateNode(x);prev->next = newnode;newnode->next = pos;}
}
链表任意位置前删除void SLErase(SLNode**pphead,SLNode* pos)
函数功能为删除pos位置的节点
这个函数和之前的函数实现方式都是差不多的,删除一个节点,就需要找到这个节点的前一个节点
void SLErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(*pphead);assert(pos);SLNode* prev = *pphead;while (prev->next != pos){if (prev->next == NULL){printf("找不到pos的位置");exit(-1);}prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;
}
这个代码过程如下图
prev->next=pos

让prev->next=pos->next

释放pos指向节点的空间

上面的代码还是少考虑了只有一个节点时的情况
prev->next为空,但是prev已经在pos所在的位置了

我们就应该加一个判断,if(*pphead=pos),然后就直接头删
代码
void SLErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos){SLPopFront(pphead);}else{SLNode* prev = *pphead;while (prev->next != pos){if (prev->next == NULL){printf("找不到pos的位置");exit(-1);}prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
链表任意位置后插入void SLInsertAfter( SLNode* pos,SLNDataType x)
之前是在pos位置之前插入,我们需要遍历链表才能找到pos位置之前的节点,所以需要传pphead,而这个函数是在pos位置后插入,所有就不需要传pphead
void SLInsertAfter( SLNode* pos,SLNDataType x)
{assert(pos);SLNode* newnode = CreateNode(x);pos->next = newnode;newnode->next = pos->next;
}
这是一段错误的代码



我们发现按照上面的逻辑pos->next其实就是newnode->next,所以在用这个函数时就会出现问题
代码
void SLInsertAfter( SLNode* pos,SLNDataType x)
{assert(pos);SLNode* newnode = CreateNode(x);newnode->next = pos->next;pos->next = newnode;
}
例题
用void SLInsertAfter( SLNode* pos,SLNDataType x)实现在pos位置前插入一个节点
思路:虽然我们不知道pos位置前一个节点的地址,但是我们可以通过这个函数在pos位置后插入一个节点,然后让这个节点的数据val和pos位置的数据val交换,就可以实现这pos位置之前插入节点




链表任意位置后删除void SLEraseAfter(SLNode* pos, SLNDataType x)
我们来看看下面一段代码
void SLEraseAfter(SLNode*pos)
{assert(pos);pos->next=pos->next->next;free(pos->next);
}
这段代码有人认为程序运行的过程如下


其实是这样的


这段代码不仅没有删除pos的下一个节点,反而让pos下一个节点的next指针变成了野指针
正确的方法是需要用tail指针保存pos->next,然后让pos->next=pos->next->next,之后再释放掉tail指向的空间
void SLEraseAfter(SLNode* pos, SLNDataType x)
{assert(pos);SLNode* tmp = pos->next;pos->next = pos->next->next;free(tmp);tmp = NULL;
}
但是我们还需要考虑到pos为尾节点的情况,因为pos->next=NULL,而pos->next->next就不知道是什么了,所以我们还需要加一下断言
代码
void SLEraseAfter(SLNode* pos, SLNDataType x)
{assert(pos);assert(pos->next);SLNode* tmp = pos->next;pos->next = pos->next->next;free(tmp);tmp = NULL;
}
销毁链表void SLDestroy(SLNode** pphead)
代码
void SLDestroy(SLNode** pphead)
{assert(pphead);SLNode* cur = *pphead;while (cur != NULL){SLNode* next = cur->next;free(cur);cur = next;}*pphead = NULL;
}
相关文章:
链表详解(三)
目录 链表功能实现链表的查找SLNode* SLFind(SLNode* phead, SLNDataType x)代码 链表任意位置前插入void SLInsert(SLNode**pphead,SLNode* pos, SLNDataType x)代码 链表任意位置前删除void SLErase(SLNode**pphead,SLNode* pos)代码 链表任意位置后插…...
【RESP问题】RESP.app GUI for Redis 连接不上redis服务器
问题描述: 在使用RESP的时候出现地址和密码正确但是连接不上Redis服务器的情况,但是由于在之前我是修改过Redis的配置文件的,所以现在怀疑是防火墙的问题。 问题解决: 在[rootlocalhost ~]下输入以下命令打开防火墙 #放通6379/…...
【github 有趣项目】AMULE
官方网站github ‘All-platform’ P2P client based on eMule电骡社区文档 下载&安装 去官方网站下载(社区版一般版本较新),解压版解压打开即可。 点击“下一页”,输入名称,后边全都下一步即可 通过upnp设置端…...
【WRF数据准备】土地利用类型分类标准:USGS+MODIS IGBP 21
【WRF数据准备】土地利用类型分类标准:USGSMODIS IGBP 21 WRF常用土地类型分类MODIS IGBP 21USGSNLCD Landuse 选择土地利用分类标准替换城市土地类型后更改土地利用分类参考 WRF常用土地类型分类 WRF中土地利用类型最高分辨率是30s,且主要分为MODIS和U…...
KVM虚拟机迁移:无缝迁徙,重塑云上未来
作者简介:我是团团儿,是一名专注于云计算领域的专业创作者,感谢大家的关注 座右铭: 云端筑梦,数据为翼,探索无限可能,引领云计算新纪元 个人主页:团儿.-CSDN博客 目录 前言&#…...
CSS常见适配布局方式
在网页设计中,布局是确保内容按预期显示的关键部分。CSS 提供了多种布局方式,每种方式都有其特定的用途和优势。以下是您提到的五种布局方式的详细解释: 1. 流式布局(百分比布局) 概述: 流式布局…...
ArkUI常用布局:构建响应式和高效的用户界面
在HarmonyOS应用开发中,ArkUI作为用户界面开发框架,提供了多种布局方式来帮助开发者构建响应式和高效的用户界面。本文将详细介绍ArkUI中的常用布局方式,包括线性布局、层叠布局、弹性布局、相对布局、栅格布局、列表和轮播布局,并…...
论面向服务架构设计及其应用
一、引言 企业应用集成(Enterprise Application Integration,EAI)是企业实现信息系统协同工作的关键途径,尤其是在当前多系统、多平台并存的企业环境下,集成需求愈发显著。面向服务架构(Service-Oriented …...
HTML5 + CSS3 + JavaScript 编程语言学习教程
HTML5 CSS3 JavaScript 编程语言学习教程 欢迎来到这篇关于 HTML5、CSS3 和 JavaScript 的详细学习教程!无论你是初学者还是有一定基础的开发者,这篇文章都将帮助你深入理解这三种技术的核心概念、语法和应用。 目录 HTML5 1.1 HTML5 简介1.2 HTML5 …...
Java日志脱敏——基于logback MessageConverter实现
背景简介 日志脱敏 是常见的安全需求,最近公司也需要将这一块内容进行推进。看了一圈网上的案例,很少有既轻量又好用的轮子可以让我直接使用。我一直是反对过度设计的,而同样我认为轮子就应该是可以让人拿去直接用的。所以我准备分享两篇博客…...
在 Ubuntu 22.04 上部署Apache 服务, 访问一张照片
要在 Ubuntu 22.04 上部署一张照片,使其可以通过 Apache 访问,你可以按照以下步骤进行操作: 1. 安装 Apache(如果尚未安装) 如果你还没有安装 Apache,可以使用以下命令: sudo apt update sud…...
从0学习React(10)
示例代码: const columns: ProColumns<API.BasicInfoItem>[] [{title: 设备编码,dataIndex: deviceCode,ellipsis: true,width: 40,},{title: 设备名称,dataIndex: deviceName,ellipsis: true,width: 50,},{title: 产线-工序,dataIndex: deviceClassifyName…...
Redis-结构化value对象的类型
文章目录 一、Redis的结构化value对象类型的介绍二、Redis的这些结构化value对象类型的通用操作查看指定key的数据类型查看所有的key判断指定key是否存在为已存在的key进行重命名为指定key设置存活时间pexpire与expire 查看指定Key的存活时间为指定key设置成永久存活 三、Redis…...
【QT】Qt对话框
个人主页~ Qt窗口属性~ Qt窗口 五、对话框2、Qt内置对话框(1)Message Box(2)QColorDialog(3)QFileDialog(4)QFontDialog(5)QInputDialog 五、对话框 2、Qt内…...
【计算机网络篇】数据链路层(14)虚拟局域网VLAN(概述,实现机制)
文章目录 🛸虚拟局域网VLAN🍔虚拟局域网VLAN的实现机制🥚IEEE 802.1Q帧🥚以太网交换机的接口类型🗒️例一:在一个交换机上不进行人为的VLAN划分,交换机各接口默认属于VLAN1且类型为Access的情况…...
伺服中的电子凸轮与追剪
一、机械凸轮 机械凸轮是一个具有曲线轮廓或凹槽的构件,它把运动特性传递给紧靠其边缘移动的推杆,推杆又带动机架做周期性运动。 凸轮的推杆位置跟随凸轮角度的周期性变化而变化,其运动特性与机械凸轮的外形相关,定义凸轮…...
Oracle 第22章:数据仓库与OLAP
第22章:数据仓库与OLAP 1. 数据仓库概念 数据仓库(Data Warehouse, DW) 是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。数据仓库中的数据通常来自不同的操作型系统或外部数据源,…...
在Ubuntu上安装TensorFlow与Keras
文章目录 1. 查看系统和Python版本信息1.1 查看Ubuntu版本信息1.2 查看Python版本信息 2. 安装pip2.1 下载get-pip.py2.2 运行get-pip.py2.3 查看pip版本 3. 安装Jupyter Notebook3.1 安装Jupyter Notebook3.2 运行Jupyter Notebook3.3 安装jupyter-core3.4 配置Jupyter Notebo…...
vue data变量之间相互赋值或进行数据联动
摘要: 使用vue时开发会用到data中是数据是相互驱动,经常会想到watch,computed,总结一下! 直接赋值: 在 data 函数中定义的变量可以直接在方法中进行赋值。 export default {data() {return {a: 1,b: 2};},methods: {u…...
如何理解ref,toRef,和toRefs
1. ref ref 是 Vue 3 提供的一个用于创建响应式数据的 API。它可以用来创建简单的响应式变量,例如数字、字符串、布尔值或对象等。通过使用ref,当数据发生变化时,相关的组件视图会自动更新。 用法 创建响应式数据: import { ref …...
蓝牙抓包不求人:从HCI日志里‘挖’出Link Key的两种实用方法(附安卓路径)
蓝牙安全逆向实战:从HCI日志中提取Link Key的深度解析在蓝牙协议安全研究领域,Link Key作为设备配对认证的核心凭证,其获取方式一直是逆向工程师关注的焦点。许多安全审计场景下,我们往往只能获得加密后的HCI通信日志,…...
AI赋能5G核心网故障诊断:从PCAP解析到智能根因分析的工程实践
1. 项目概述:当AI遇见5G核心网故障诊断在5G核心网的运维与测试一线干了这么多年,最头疼的莫过于面对海量的PCAP抓包文件。一个复杂的信令流程下来,动辄几千甚至上万个数据包,工程师需要像侦探一样,逐帧审视协议交互&am…...
基于MaixCam的延时摄影系统:从硬件选型到Python编程全解析
1. 项目概述:用MaixCam打造你的专属延时摄影工坊延时摄影,这个听起来有点专业、甚至带点“魔法”色彩的词,其实离我们并不遥远。想想看,把一朵花从含苞到绽放的几天时间,压缩成十几秒的惊艳绽放;或者把一座…...
HDI 高密度互连板阶数的深度理解
一、概述高密度互连板(High Density Interconnector, HDI)是通过激光微孔技术和逐层积层工艺实现高密度布线的印制电路板。其阶数划分是行业内统一的技术标准,核心依据为独立积层压合次数与配套激光盲孔制程次数,而非单面层数或钻…...
2026年一键生成论文工具对比实测:5款神器从选题到格式全流程护航
写论文的焦虑,是每个科研人和学生都心照不宣的“隐形压力”。选题无从下手,文献检索耗时费力,逻辑框架反复推翻,格式排版让人抓狂,查重降重更是像在和系统玩“猫鼠游戏”。2026年的AI工具早已不是过去那种“打字机”&a…...
3大技术突破:重新定义Switch游戏安装性能极限
3大技术突破:重新定义Switch游戏安装性能极限 【免费下载链接】Awoo-Installer A No-Bullshit NSP, NSZ, XCI, and XCZ Installer for Nintendo Switch 项目地址: https://gitcode.com/gh_mirrors/aw/Awoo-Installer Awoo Installer是一款专为破解版Nintendo…...
8款网盘直链下载助手:彻底告别限速烦恼,实现高速下载自由
8款网盘直链下载助手:彻底告别限速烦恼,实现高速下载自由 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移…...
告别RaiDrive广告!用开源rclone+Alist,免费把阿里云盘/百度网盘变成电脑本地硬盘
开源方案实战:用rcloneAlist打造无广告的云盘本地化体验 每次打开RaiDrive时弹出的广告窗口是否让您感到困扰?商业软件的收费模式是否让您犹豫不决?今天,我们将彻底解决这些问题。通过开源工具Alist和rclone的组合,您不…...
SpringBoot WebClient 介绍
目录一、什么是 WebClient?二、 WebClient 能解决什么问题?三、WebClient 和 RestTemplate 的区别四、WebClient 的核心优势1. 非阻塞(Non-Blocking)2. 支持异步3. 链式 API 更现代五、WebClient 的核心对象六、Mono 和 Flux 是什…...
PrediPrune:机器学习驱动的编译器超级优化候选剪枝策略
1. 项目概述与核心挑战在编译器优化的世界里,我们总在追求极致的性能。传统的编译器优化器,比如LLVM的Pass,依赖于一系列预定义的、经过验证的转换规则。它们很高效,但想象力也受限于这些规则。超级优化器(Superoptimi…...
