链表详解(三)

目录

    • 链表功能实现
      • 链表的查找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;
}

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

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

相关文章

《C#语法一篇通》,20万字,48小时阅读,持续完善中。。。

本文摘录了C#语法的主要内容,接近20万字。 所有鸡汤的味道都等于马尿! 如果你相信任何所谓的鸡汤文章,智商堪忧。 计算机语言没有”好不好“之说,骗子才会告诉你哪个语言好,学好任何一本基础语言(C&#…

HarmonyOS开发 - 餐饮APP中多门店多窗口打开实例补充

specified启动模式为指定实例模式,有一些特殊场景,例如多门店应用中每次打开一个门店都希望能新建一个门店实例,而重复打开同一个门店都是同一门店实例。 此篇为餐饮APP中多门店实例的补充内容,以解决同一门店多次点击重复创建新窗…

Lammps动态删除反应产物分子的方法

大家好,我是小马老师。 本文介绍lammps反应势模拟过程中动态删除产物分子的方法。 lammps reaxff反应势可以模拟分子间的化学反应,fix reaxff/species命令可以输出反应过程中的产物信息。 在2022之后的lammps版本中,这个命令新增了delete关键词,使用这个命令,可以动态删除…

【Redis问题】(error) NOAUTH Authentication required.解决方法

问题描述: “(error) NOAUTH Authentication required”,通过ping方法和redis打招呼发现不成功,意思是没有这个权限(也就是没有密码) 问题解决1: 在命令后面加上密码(一般情况下,在…

css 在 hover 子元素时,不要让父元素触发 hover 效果

别人写的没看到一个能好用的&#xff0c;虽然功能简单&#xff0c;但是这个应该还是比较陌生的 希望帮到你&#xff0c;能帮我点个赞 废话不多书&#xff0c;直接出代码 <div class"parent"><div class"child">悬停我</div></div&g…

关于自动化测试用例失败重试的一些思考

自动化测试用例失败重跑有助于提高自动化用例的稳定性&#xff0c;那我们来看一下&#xff0c;python和java生态里都有哪些具体做法&#xff1f; 怎么做 如果是在python生态里&#xff0c;用pytest做测试驱动&#xff0c;那么可以通过pytest的插件pytest-rerunfailures来实现…

Vue Router进阶详解

导航守卫 若依框架登录鉴权详解&#xff08;动态路由&#xff09;_若依鉴权-CSDN博客 完整的导航解析流程 导航被触发&#xff1a; 当用户点击页面中的链接、使用编程式导航&#xff08;如router.push或router.replace&#xff09;或手动输入URL时&#xff0c;导航流程被触发。…

如何把子组件的v-model修改数据,进行接收然后定义数据格式,子传父的实现

在 Vue 中&#xff0c;实现子组件通过 v-model 向父组件传递数据并接收后进行格式化&#xff0c;可以按照以下步骤来封装和实现&#xff1a; 步骤 1: 子组件实现 v-model 子组件需要定义一个 props 来接收 v-model 的值&#xff0c;并通过 emit 方法发出更新事件。 <!-- …

Rust 力扣 - 1984. 学生分数的最小差值

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 原数组 nums 排序&#xff0c;遍历nums中下标为[0, nums.len() - k]的学生分数 假设当前遍历的下标为i则&#xff0c;以 i 下标为最小值的学生分数的最小差值为nums[i k - 1] - nums[i] 取最小差值的最小值即…

distrobox install in ubuntu 22.04 / 在 ubuntu 22.04 上安装 distrobox (***) OK

要点&#xff1a; 本测试实验&#xff0c;采用的是 podman distrobox 在沙盒 snap 中&#xff0c;安装 distrobox 需要使用 --devmode 开发模式&#xff1b;可以避开 distrobox 的版本检查&#xff1f; distrobox 官方文档显示&#xff0c; Installation https://distrobox.i…

【k8s】-运维技巧-1

文章目录 k8s-节点驱逐节点驱逐节点删除批量删除镜像k8sdockerdocker 删除缓存k8s异常Pod清理删除所有命名空间下非Running状态的 Pod强制删除所有命名空间下非Running状态的 Podk8s-节点驱逐 节点驱逐 kubectl cordon k8s-node1 kubectl drain k8s-node1 --delete-local-dat…

开源数据库 - mysql - 基于GTID的主备部署

GTID AUTO_POSITION MODE的主从 搭建主从模式 注意&#xff0c;主备库必须开启GTID并设置好server_id&#xff1a; enforce_gtid_consistency ON # 开启强制GTID一致性&#xff0c;防止非GTID事务复制 gtid_mode ON # 开启GTID server_id 9910 # 主主或者主从配置必须不一…

IA应用加速,让电子供应链更智能高效

在当今数字化浪潮中&#xff0c;电子产品制造行业正经历着前所未有的变革。越来越多的企业开展全球化业务&#xff0c;进行数字化转型&#xff0c;对于网络时延的需求也更高。 客户背景 客户专注于为中小微电子产品制造企业提供产品技术方案开发、电子元器件采购、PCBA生产制造…

git 删除远程不存在本地命令却能看到的分支

要删除远程不存在但本地却能看到的分支&#xff0c;你可以按照以下步骤操作&#xff1a; 删除本地分支&#xff1a; 如果你确定要删除的分支已经没有用处&#xff0c;可以使用以下命令来删除本地分支&#xff1a; git branch -d <branch-name>这里的 <branch-name>…

2024年11月4日Github流行趋势

项目名称&#xff1a;DS4SD / docling 项目维护者&#xff1a;dolfim-ibm, github-actions, vagenas, cau-git, PeterStaar-IBM 项目介绍&#xff1a;让您的文档准备好迎接生成式AI。 项目star数&#xff1a;3,906 项目fork数&#xff1a;221 项目名称&#xff1a;abi / scree…

从模糊搜索到语义搜索的进化之路——探索 Chroma 在大模型中的应用价值

目录 从模糊搜索到语义搜索的进化之路——探索 Chroma 在大模型中的应用价值 一、引言 二、实现语义搜索的数据库 Chroma 1、语义搜索是什么 2、Chroma 语义搜索的原理 三、如何在项目中应用 Chroma 1、Chroma 的实际应用场景 2、安装Chroma&#xff08;python环境&…

iOS灵动岛动画小组件怎么播放动画

这个灵动岛相关的展示位置分几个地方&#xff1a; 紧凑型&#xff0c;最小化&#xff0c;扩展型&#xff0c;还有锁屏位置 我们先来看一下我这边实现的动画效果 demo下载&#xff1a; iOS灵动岛GIF动画 灵动岛样式 灵动岛有三种渲染模式&#xff1a; 第一种是 紧凑型&…

网络信息系统的整个生命周期

网络信息系统规划 此阶段主要是根据企业的业务需求、技术发展趋势以及市场环境等因素&#xff0c;对网络信息系统进行初步的规划和设计。规划的内容可能包括系统的目标、功能、性能、安全性等方面的要求。 规划阶段还需要进行可行性研究&#xff0c;评估项目在技术、经济、社…

力扣排序268题 数字丢失

题目&#xff1a; 丢失的数字 给定一个包含[0,n]中n各数的数组nums&#xff0c;找出[0,n]这个范围 内没有出现在数组中的那个数。 示例1&#xff1a; 输出&#xff1a;n 3,因为有3个数字&#xff0c;所以所有的数字都在范围 [0,3]内。2是丢失的数字&#xff0c;因为它没有出现…

Java实现动态切换ubuntu壁纸功能

1.在一个文件夹放好图片 2.读取文件夹的图片路径&#xff0c;放入数组 3.调用命令将图片逐个设置为壁纸 使用 Java 在 Ubuntu Linux 系统中实现自动切换壁纸的示例程序。这个程序使用了gnome-desktop-item-edit命令来设置壁纸&#xff0c;并通过定时任务来定期切换壁纸 impor…