一篇博客读懂单链表——Single-List

news2025/1/16 14:07:23

目录

一、初识单链表

单链表是如何构造的:

单链表如何解决顺序表中的问题:

二、单链表的初始定义 

三、尾插和头插

3.1 新建结点CreateNode

3.2 打印SLTPrint

3.3 尾插SLTPushBack 

3.4 头插SLTPushFront 

四、尾删和头删

4.1 尾删SLTPopBack

4.2 头删SLTPopFront

五、某位置前插入删除

5.1 查找SLTFind

5.2 某位置前插入SLTInsert

5.3 某位置删除SLTErase

六、某位置后插入删除

七、 assert断言 

7.1 SLTPrint断言

7.2 SLTPushBack 和 SLTPushFront 断言

7.3 SLTPopBack 和 SLTPopFront 断言

7.4 SLTInsert 和 SLTErase 断言


顺序表的问题:

1.尾部插入效率还不错,头部或者中间插入删除,需要挪动数据,效率低下

2. size 满了后只能扩容,扩容是有一定消耗的;扩容一般存在一定的空间浪费

我们总结了顺序表的问题,下面我们学习的单链表就可以让这些问题迎刃而解。 

学习要求:掌握C语言的指针、二级指针、动态开辟内存的知识

一、初识单链表

我们首先来回顾一下顺序表中的结构体成员:

顺序表中单个结构体存储着顺序表的表头指针、整个顺序表的数据容量、实际存储的数据容量......

单链表是如何构造的:

链表每个结点会存储一个数据,同时会存储一个指针,这个指针指向下一个结点。

单链表如何解决顺序表中的问题:

链表的每一个结点都是 malloc 出来的,他们的地址并不是连续分配的,如果从头部或者中间插入删除,只需要改动单个结点,无需挪动整个 table ;因为链表的结点是一个一个开辟的,所以也不存在顺序表的空间浪费。

下面我们来看一下链表实际的结构:

在这里我们强调不需要太过于关注链表的物理结构,我们的注意点应该集中在其是个结构体。

二、单链表的初始定义 

这里还是和顺序表一样的套路,把它当成一个工程去做,当然就要分文件啦。

其次,在 .h 文件中我们要做我们的准备工作:

 接下来我们来看一下我们需要对链表做的操作:

三、尾插和头插

在学习尾插之前,我们需要先思考一个问题,为什么在我们的操作中有时候需要传指针而有时候需要传二级指针呢(下面以尾插为例)?

如果要探索这个问题的答案,我们一定要知道我们在尾插时是需要改变指针的值的。如果我们调用函数需要改变 int 的值,我们是不是需要传 int* 来改变我们的实参,而在这里我们需要改变的是SLTDataType* 的值,所以就需要我们传入指针的地址来对指针的值进行修改。

3.1 新建结点CreateNode

 我们在这还需要写一个函数,这个函数可以说是链表插入元素的精髓了,我们在前面了解到了链表插入元素是一个结点一个结点地 malloc 出来的,所以我们在这里写一个 CreateNode 函数来创建我们的新结点,可以极大地减少我们下面的代码量。

3.2 打印SLTPrint

为了方便后续的检测,我们也要先把显示链表内容的函数定义出来:

3.3 尾插SLTPushBack 

3.4 头插SLTPushFront 

这里要注意的是我们先让 NewNode 作为头结点来指向 *pphead (即phead) 以此来找到之前第一个结点的地址,然后我们再将 *pphead 指向 NewNode,在以后遇到需要头插的题目,我们也要使用这种顺序。

四、尾删和头删

4.1 尾删SLTPopBack

尾删肯定要找到最后一个元素,让指向最后一个元素的指针直接指向 NULL ,然后再 free 掉最后一个结点,再查找会后一个结点时,我们需要改变的是指向它地址的指针的值,所以我们就要用 tail->next->next != NULL 作为寻找条件:

欸,我们从图中和代码中不难发现,当 tail 的后两个元素为 NULL 时才能进行判断,那么如果链表中只有一个结点(tail->next == NULL),这时候应该怎么办呢?当然是单独进行判断啦!

下面我们来看一下完整代码:

但是需要注意的是,如果我们在一开始就定义了 tail 指针指向 *pphead ,当 tail == NULL 时, free 掉 tail ,这样的写法在我们删掉最后一个结点时会在 Print() 函数中报错,错误代码如下:

这是为什么呢?原因是如果提前用 tail 接收 *pphead,那么在 free(tail) 后也应该将 *pphead 置为空,如果仅仅将 tail 置空,那我们的 *pphead 就成了野指针,这样当然是不可取的。

4.2 头删SLTPopFront

头删就相对简单很多,只需要改变 *pphead 的指向,再 free 就好。

 

五、某位置前插入删除

在学习某位置插入删除前,我们需要知道的是我们的“某位置”应该如何定位,比较某个结点与我们的目标值?显然是不行的,当有多个结点的值重复时这个想法自然就会被推翻,那我们应该怎么办呢?我们也要设计一个查找函数,将第一个查找到的结点地址返回,再将该地址传入我们的插入删除函数中。

5.1 查找SLTFind

当我们遍历完整个链表却没找到想要的元素时,说明链表中没有该元素,返回 NULL 。

5.2 某位置前插入SLTInsert

首先我们要判断这里是不是头插,如果是头插,我们直接调用头插函数,如果不是,我们继续我们的移形换影大法,插入结点。 

 

在改变 cur->next 前,我们要用临时变量 tmp 接收 cur 下一个结点的地址,这样才能在后面找到。 

5.3 某位置删除SLTErase

因为我们的 SLTFind 函数可以定位到 val==x 的当前结点,所以我们可以用这一特点删除指定位置的元素。当我们要删除的是第一个结点时,我们同样可以调用头删函数。

 

 

六、某位置后插入删除

这里思路比较简单,我们来简单看一下代码:

七、 assert断言 

最后我们当然不能忘记断言,下面我们一起来看看每个函数中需要的断言:

7.1 SLTPrint断言

SLTPrint不用断言,因为我们已经设置了当链表为空时只需打印 NULL

7.2 SLTPushBack 和 SLTPushFront 断言

这两个函数只需要断言 pphead 这个二级指针,因为我们是允许链表为空时的头插尾插的。

7.3 SLTPopBack 和 SLTPopFront 断言

这两个函数不仅要断言 pphead 还要断言 *pphead ,因为当链表中有结点时才可以Pop

7.4 SLTInsert 和 SLTErase 断言

插入和删除函数不仅需要像上面两个函数一样断言 pphead *pphead ,还要断言 pos

下面是我的单链表源代码库,包含了对每个函数测试用的代码,需要的uu可以自行查看:

手撕单链表 - Gitee.com

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

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

相关文章

容器数据卷+MYSQL实战

什么是容器数据卷? 让我们回忆一下docker理念: 就是将应用和环境打包成一个镜像 数据? 如果数据都在容器中,那么我们删除容器,数据就会丢失 !需求:数据持久化就完美了 对于MYSQL&#xff0…

玄子Share-Git 入门手册

玄子Share-Git 入门手册 简单介绍 Git Git 是一个自由和开源的分布式版本控制系统,旨在快速和高效地处理从小型到大型的所有项目 Git 简单易学,占用空间小,性能快如闪电。它比Subversion、CVS、Perforce和ClearCase等SCM工具更有优势&…

python默认的输入类型是字符串,怎样转换为其他的类型

在Python中,默认的输入类型是字符串(str类型)。无论你输入的是数字、字符还是其他类型的内容,input函数都会将其作为字符串处理并返回。 如果需要将字符串转换为其他类型(如整数、浮点数等),可…

kafka和rocketMq的区别

kafka topic 中每一个分区会有 Leader 与 Follow。Kafka 的内部机制可以保证 topic 某一个分区的 Leader 与 Follow 不在同一台机器上 Leader 节点承担一个分区的读写,Follow 节点只负责数据备份 如果 Leader 分区所在的 Broker 节点宕机,会触发主从节…

Linux 的热插拔机制通过 Udev(用户空间设备)实现、守护进程

一、Udev作用概述 udev机制简介udev工作流程图 二、Linux的热拔插UDEV机制 三、守护进程 守护进程概念守护进程在后台运行基本特点 四、守护进程和后台进程的区别 一、Udev作用概述 udev机制简介 Udev(用户空间设备)是一个 Linux 系统中用于动态管…

玄子Share-HTML5知识手册

玄子Share-HTML5知识手册 前言: 这一版 HTML 笔记,算是我写的第四版了,第三版对照课本编写,第四版则是对照 MDN 官方文档编写,不论是术语亦或专业性,都更上一层 文章依托 MDN 文档,拓展了大量课…

【2023-11-09】git使用随记——gitignore文件配置某些文件忽略

git使用随记——gitignore文件配置某些文件忽略 通过git进行版本控制在项目中是非常常见的,一些项目构建上的文件通常是不需要进行版本控制的,也就无需推送到git仓库中,比如前端项目中的node_module目录。提供配置.gitignore文件 但是某些情…

腾讯云88,阿里云99,现在都这么卷了吗?!

你是否曾经想过,云服务器的价格竟然可以如此亲民?现在,腾讯云和阿里云竟然都推出了超低价位的云服务器,只要88元和99元!这让我们这些自媒体人、创业者、开发者等都感到非常惊喜。可以看一下配置和价格: 可…

MATLAB中Line 属性说明

目录 颜色和样式 位置 Line 属性是注释线条的外观和行为。 Line 属性控制 Line 对象的外观和行为。通过更改属性值,可以修改线条的特定方面。使用圆点表示法查询和设置属性。 h annotation("line"); c h.Color; h.Color "red"; 颜色和样…

边缘计算如何改变数据存储?

边缘计算在整个价值链中提供多种优势——从降低成本到提高效率再到安全数据传输。该技术允许在源头收集和分析相关数据,这有助于减少延迟和带宽成本,同时显著提高计算过程的冗余系数和效率。 通过降低数据传输成本和损失,边缘计算帮助企业实现…

docker更改存储目录原因及方案

为什么一定要将docker的存储目录挂载到其他目录 docker在安装时默认存储目录在/var/lib/docker,而该目录是在系统盘下的。docker安装后,会使用各种各样的镜像,动辄几个G,那么如此多的镜像文件,装着装着系统盘就撑爆了…

【AI】自回归 (AR) 模型使预测和深度学习变得简单

自回归 (AR) 模型是统计和时间序列模型,用于根据数据点的先前值进行分析和预测。这些模型广泛应用于各个领域,包括经济、金融、信号处理和自然语言处理。 自回归模型假设给定时间变量的值与其过去的值线性相关,这使得它们可用于建模和预测时…

【Unity细节】Unity中的Transform.SetParent还有你不知道的细节

👨‍💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 😶‍🌫️收录于专栏:unity细节和bug 😶‍🌫️优质专栏 ⭐【…

除了鲁大师,你还可以装这些。

无论是专业的电脑维护人员,还是只是一个简单的 DIY 兴趣爱好者,有的时候都会或多或少遇到一些电脑硬件的问题。 这种情况下,如果特地去安装一款专门的检测软件,肯定会非常麻烦。 一般来说,检测电脑硬件,使…

【解刊】可投!Elsevier旗下1/2区SCI,4天见刊!

计算机类 • 好刊解读 今天小编带来Elsevier旗下计算机领域好刊的解读,如有相关领域作者有意向投稿,可作为重点关注!后文有同领域快刊发表案例,供您投稿参考~ 01 期刊简介 Sustainable Computing: Informatics and Systems ☑️…

内存卡数据恢复,5 个免费好用的数据恢复方法工具全解

丢失了 SD 卡中的一些重要照片或文档,并且不知道如何恢复?好吧,别担心!!以下是一些适用于 Windows 的最佳 SD 卡恢复工具,可增加您检索意外删除、丢失或丢失数据的机会。 什么是 SD 卡数据恢复软件&#xf…

ps 让图片附着在文字上

按住alt在文字与图片图片中间,文字在图片下面)

Scala中编写多线程爬虫程序并做可视化处理

目录 一、引言 二、Scala爬虫程序的实现 1、引入必要的库 2、定义爬虫类 3、可视化处理 三、案例分析:使用Scala爬取并可视化处理电影数据 1、定义爬虫类 2、实现爬虫程序的控制逻辑 3、可视化处理电影数据 四、总结 一、引言 随着互联网的快速发展&#…

CSS3 过度效果、动画、多列

一、CSS3过度&#xff1a; CSS3过渡是元素从一种样式逐渐改变为另一种的效果。要实现这一点&#xff0c;必须规定两相内容&#xff1a;指定要添加效果的CSS属性&#xff1b;指定效果的持续时间。如果为指定持续时间&#xff0c;transition将没有任何效果。 <style> div…

华为认证 | 11月底这门HCIP认证即将发布!

非常荣幸地通知您&#xff0c;华为认证HCIP-Storage V5.5&#xff08;中文版&#xff09;预计将于2023年11月30日正式对外发布。为了帮助您做好学习、培训和考试计划&#xff0c;现进行预发布通知&#xff0c;请您关注。 01 发布概述 基于“平台生态”战略&#xff0c;围绕“云…