C语言—单链表

目录

一、链表的概念及结构

二、单链表实现

(2.1)基本结构定义

(2.2)申请节点

(2.3)打印函数

(2.4)头部插入删除\尾部插入删除

(2.4.1)尾部插入

(2.4.2)头部插入

(2.4.3)尾部删除

(2.4.4)头部删除

(2.5)查找

(2.6)在指定位置之前插入数据

(2.7)删除pos节点

(2.8)在指定位置之后插入数据

(2.9)删除pos之后的节点

(2.10)销毁链表

三、链表的分类


一、链表的概念及结构

概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表的结构跟⽕⻋⻋厢相似,如图所示:

与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“结点/节点”。节点的组成主要有两个部分:当前节点要保存的数据和保存下⼀个节点的地址(指针变量)。图中指针变量 plist 保存的是第⼀个节点的地址,我们一般称之为头指针。链表中每个节点都是独⽴申请的(即需要插⼊数据时才去申请⼀块节点的空间),所以我们需要通过指针变量来保存下⼀个节点位置才能从当前节点找到下⼀个节点。

补充说明:
1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续
2、节点⼀般是从堆上申请的
3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

二、单链表实现

注意:这里的单链表指的是不带头单向不循环链表(后面会介绍链表的分类)

(2.1)基本结构定义

为了单链表的复用性,这里将 int 重命名为SLTDataType,单链表节点中包含一个数据域(data),一个指针域(next),数据域存放需要存放的数据,指针域存放下一个节点的地址。这里为了方便后续使用又将struct SListNode 重命名为SLTNode。

(2.2)申请节点

后续插入节点的函数都需要先申请节点,所以这里直接写一个申请节点的函数,方便后面使用,在该函数中,首先用malloc申请一块节点大小的空间,然后判断是否申请成功,如果申请失败,用perror函数报错,然后结束程序,如果申请成功,将数据插入,并将指针域置空,然后返回节点地址。(形参是要插入的数据)

(2.3)打印函数

为了后续测试中方便查看单链表中的数据,我们这里实现一个打印函数,功能是打印单链表中的所有数据。

具体实现:

解释:形参是指向整个单链表头部的指针,要打印单链表中的数据,首先单链表不能为空,如果是空表没有数据可打印,所以先断言头指针不为空,然后定义一个临时变量 cur 记录链表头部的地址(即将头指针赋给cur),然后以 cur 作为循环条件,每次进入循环,打印当前节点存储的数据,打印后将指针向后移动,当打印完最后一个节点数据后,指针移动到NULL,循环结束。最后可以再打印一个换行。

(2.4)头部插入删除\尾部插入删除

(2.4.1)尾部插入

具体实现:

思路:遍历找到尾节点,然后将新节点连接上,如果链表为空,直接将新节点作为头。

解释:该函数中有两个形参,一个是二级指针,指向单链表的头指针,一个是要插入的数据,首先这里的二级指针一定不可以为空,如果二级指针为空,我们将无法找到链表,所以先断言二级指针不为空,然后判断当前链表是否为空,如果链表为空,直接将新申请的节点置为表头(即直接让头指针指向该节点,注意这里的pphead是二级指针,要先解引用才是头指针),如果链表不为空,先定义一个临时指针指向链表头部,然后当 cur 的 next 不为空的时候进行循环,出循环时 cur 一定指向尾节点,因为链表中每个节点(除尾节点)中的指针域存的都是下一个节点的地址,而最后一个节点后面已经没有节点了,所以它的指针域存的是NULL,而循环结束的条件就是 cur 指针域为NULL。找到尾节点后,将新申请的节点插入即可。

图解:

测试:

(2.4.2)头部插入

具体实现:

思路:将新节点插在原链表的头的前面,并作为新的头。

解释:该函数中有两个形参,一个是二级指针,指向单链表的头指针,一个是要插入的数据,首先这里的二级指针一定不可以为空,如果二级指针为空,我们将无法找到链表,所以先断言二级指针不为空,然后直接申请新节点,新节点的指针域存原链表的头,然后将新节点赋给头指针,这样新节点就插入到原链表的头部了并且成为新链表的头。

图解:

测试:

(2.4.3)尾部删除

具体实现:

思路:遍历找到尾节点的前一个节点,释放尾节点,然后将尾节点的前一个节点指针域置空,如果链表只有一个节点,直接释放就行,但是要将头指针置空。

解释:该函数只有一个参数,即指向头指针的二级指针,这里和上面一样二级指针一定不可以为空,又因为是删除,链表一定要存在,所以头指针也不可以为空,所以先断言这两个指针不为空,尾删分为两种情况,当链表只有一个节点时,删除后链表为空,这时要将头指针置空,当链表不止一个节点时,通过循环找到尾节点的前一个节点,然后释放最后一个节点,因为尾节点已经被释放了(即将空间还给操作系统了),所以尾节点的前一个节点的 next 指针要置空,成为新的尾节点,这里之所以要找尾节点的前一个节点,而不直接找尾节点也是因为 next 指针置空的问题,如果直接找尾节点,因为是单链表,无法获得它的前驱节点,想要将前驱节点的 next 指针置空就会变麻烦。

图解:

测试:

(2.4.4)头部删除

具体实现:

解释:该函数只有一个参数,即指向头指针的二级指针,和前面一样,二级指针一定不可以为空,又因为该函数的功能是删除,所以链表不可以为空,即头指针不可以为空,所以先断言这两个指针不为空,然后定义一个临时指针变量记录头节点的下一个节点的地址,释放当前头节点,将记录的后一个节点变为新的头节点,这里一定要先去记录后一个节点的位置,否则释放后就找不到了。如果当前链表只有一个节点也没事,因为它的指针域里存的是NULL,释放头节点后就将NULL给了头指针,这时链表就变为空链表了。

图解:

测试:

(2.5)查找

思路:遍历链表,对比数据域,如果相同则找到,返回节点地址,如果遍历结束还没有找到,则链表中没有该数据,返回空。

功能:给出链表的头指针和要查找的数据,该函数会在该链表中进行查找,如果找到,返回存储该数据的节点,如果没找到,返回NULL。

具体实现:

解释:该函数一共两个形参,第一个是链表的头指针,第二个是要查找的数据,因为后面要进行遍历链表,而头指针要始终指向链表头部,不能随意移动,所以先定义一个临时变量记录头指针,然后作为循环条件进行遍历,对比数据,一样就找到了,返回节点,结束函数,不一样就向后移动,访问下一个节点,如果遍历结束还没有找到说明链表中没有该数据,返回NULL。

图解:

测试:

(2.6)在指定位置之前插入数据

具体实现:

解释:该函数有三个参数,第一个是指向头指针的二级指针,第二个是指定节点位置(可以配合查找函数确定),第三个要存储的数据。二级指针和前面一样,还是不可以为空,否则找不到链表,指定位置之前插入,其实就是指定节点之前插入,所以这个节点必须存在,又因为这个节点的存在,所以链表一定不为空,所以先进行这三个条件的断言,这里插入分为两种情况,第一种情况,当pos是头节点的时候,在头节点之前插节点,其实就是前面的头插法,这里可以调用前面写好的头插函数(SLTPushFront),也可以像我一样自己写一下,第二种情况,当pos不是头节点的时候,我们需要遍历链表,找到pos的前驱节点,然后将新节点插入,因为这是单链表,所以我们没法通过pos节点向前插入节点,必须找到他的前驱节点。

图解:

(2.7)删除pos节点

思路:两种情况,第一种:要删除的是头节点时,因为头节点被删除,所以头指针要进行移动。第二种:删除的不是头节点,头指针不需要移动,如果先删除pos节点,我们就没有办法找到pos节点后面的节点了,所以先找到pos节点的前驱节点,然后将pos节点的前驱节点和后面的节点连接起来,再去删除pos节点。

具体实现:

(2.8)在指定位置之后插入数据

思路:首先想在pos位置之后插入数据,pos节点一定要存在,然后先让新节点的指针域存pos节点后面节点的地址,然后再让pos节点的指针域存新节点的地址,顺序不能颠倒,如果先让pos节点指针域存新节点,那么就无法找到pos后面的节点。

具体实现:

(2.9)删除pos之后的节点

思路:删除pos节点之后的节点,pos节点必须存在,正常情况下,pos后面那个要删的节点也必须存在,所以可以对这两个节点都断言一下,这里我用了 if 语句,允许pos后面没有节点,当没有节点时,函数不会做任何事,直接结束,如果有,先将要删除的节点记录下来,然后将pos和要删除的节点的后面的节点连接起来,如果前面没有记录要删除的节点,这里连接后就找不到要删除的节点了。然后释放要删除的节点就可以了。

具体实现:

(2.10)销毁链表

思路:要找到链表,二级指针不能为空,如果当前链表已经为空了,函数结束,不会做任何事;如果链表不为空,定义两个指针,一个记录当前要释放的节点,一个记录释放的节点后面的节点,因为释放后就无法找到它后面的节点了,这里先记录下来,这样就可以在删除节点后将记录要释放节点的指针移动到链表的下一个节点,然后继续释放,直到循环到所有节点都被释放,这里当cur为空时,已经释放了所有节点,next 此时也为空,没必要再向后移动了,所以用 if 判断一下。

具体实现:

三、链表的分类

链表的结构⾮常多样,以下情况组合起来就有8种(2x2x2)链表结构:

虽然有这么多的链表的结构,但是我们实际中最常⽤还是两种结构:单链表和双向带头循环链表。

1.⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结构的⼦结构,如哈希桶、图的邻接表等等。另外这种结构在笔试⾯试中出现很多。
2.带头双向循环链表:结构最复杂,⼀般⽤在单独存储数据。实际中使⽤的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使⽤代码实现以后会发现结构会带来很多优势,实现反⽽简单了。

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

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

相关文章

计算机毕业设计 基于Python的人事管理系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

spring揭秘26-springmvc06-springmvc注解驱动的web应用

文章目录 【README】【1】springmvc注解驱动web应用【1.1】springmvc注解驱动web应用的3个组件【1.2】springmvc注解驱动web应用代码实践 【2】springmvc常用注解【2.1】Controller注解(标注处理器类)【2.2】RequestMapping注解(标注处理器类…

OpenAI董事会主席Bret Taylor的Agent公司Sierra:专注于赋能下一代企业用户体验

本文由readlecture.cn转录总结。ReadLecture专注于音、视频转录与总结,2小时视频,5分钟阅读,加速内容学习与传播。 视频来源 youtube: https://www.youtube.com/watch?vriWB5nPNZEM&t47s 大纲 介绍 欢迎与介绍 介绍Bret Taylor&#x…

如 有 任 何 问 题 ,请 及 时 联 系 我 们 反 馈 !

如有任何问题, 请及时联系我们反馈 !https://support.qq.com/products/671606 如有任何问题, 请及时联系我们反馈 !

基金好书入门阅读笔记《基金作战笔记:从投基新手到配置高手的进阶之路》2

买基金,说到底是买基金所持有的一揽子资产。那么,常见的可投资产都有哪些类型呢? 图2.9进行了系统性的梳理,我们把资产分为四大类,分别是权益类、固收类、现金和另 类,下面就一一解读。 年化收益率是把一段…

Bluetooth Channel Sounding中关于CS Procedure的详细介绍

目录 BLE CS 过程定义: BLE CS 过程的组成部分 开始一个BLE CS 过程 与BLE CS过程相关的参数设置 BLE CS 过程定义: BLE 的CS特性包含一组LL层和空口协议的组合过程,该过程可以使得两个BLE 设备以紧密互锁的方式,在多个信道上…

人工智能专业就业方向与前景

随着产业结构升级的持续推进,未来行业领域对于人工智能专业人才的需求量会逐渐增加,一部分高校也开始陆续在本科阶段开设人工智能专业,以缓解人工智能领域人才缺口较大的问题。下面是小编整理的人工智能专业就业方向与前景,欢迎阅…

第五节——转移表(让你不再害怕指针)

文章目录 制作简易计算器什么是转移表?switch函数实现函数指针数组实现 制作简易计算器 要求:制作一个简易计算器,可以进行* / - 等功能运算。 什么是转移表? 指的就是通过函数指针数组的方式通过数组去调用里面的函数&#x…

儿童需要学习C++多久才能参加信息学奥赛的CSP-J比赛?

信息学奥赛(NOI)是国内编程竞赛领域的顶尖赛事,而对于初学者来说,参加NOI的第一步通常是通过CSP-J(全国青少年信息学奥林匹克联赛初赛),这也是面向青少年程序员的入门级竞赛。作为信息学奥赛的基…

DDR6 或将出炉 (含 LPDDR6, CAMM)

前记:目前DDR6 尚未问世,文中较多信息是“据说”,笔者也无法考证,请大家后期以JEDEC官方为准。 很多朋友可能还没用上DDR5,但不好意思的是,DDR6 可能马上就要出现了。 三星和海力士较早开始DDR6 的设计,预计2025年商业化。 DDR6 速度 来源: 半导体观察 DDR6的速度…

AcWing 662:点的坐标 ← 结构体 or 三目运算符

【题目来源】https://www.acwing.com/problem/content/664/【题目描述】 给定两个保留一位小数的浮点数 X,Y,用来表示一个点的横纵坐标。 请你判断该点在坐标系中的位置。 【输入格式】 共一行,包含两个浮点数 X,Y,表示点的横纵坐标。【输出格…

毕业设计 深度学习社交距离检测系统(源码+论文)

文章目录 0 前言1 项目运行效果2 设计原理3 相关技术3.1 YOLOV43.2 基于 DeepSort 算法的行人跟踪 4 最后 0 前言 🔥这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,…

卷积层是如何学习到图像特征的?

你好啊,我是董董灿。 想搞懂这个问题,需要先了解我们所说的特征指的是什么?然后再了解卷积核是如何学到的特征。 我们一步步来。 1、我们先来理解图像的特征 对于一张原始图像而言,说原始图像是相对于经过卷积处理而言的。 对…

VL53L4CD液位监测(2)----液位检测

VL53L4CD液位监测.2--液位检测 概述视频教学样品申请完整代码下载硬件准备STSW-IMG039容器特性包含必要的头文件变量定义测距函数 Ranging()液位误差补偿函数 Liquidlevelmeasureerrorcomponsate()数据轮询函数 get_data_by_polling()演示 概述 液位检测在工业自动化、环境监测…

十大时间序列预测模型

目录 1. 自回归模型 原理 核心公式 推导过程: 完整案例 2. 移动平均模型 原理 核心公式 推导过程: 完整案例 3. 自回归移动平均模型 原理 核心公式 推导过程: 完整案例 4. 自回归积分移动平均模型 原理 核心公式 推导过程 完整案例 5. 季节性自回归积分…

LeetCode讲解篇之695. 岛屿的最大面积

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们遍历二维矩阵,如果当前格子的元素为1进行深度优先搜索,将搜索过的格子置为0,防止重复访问,然后对继续深度优先搜索上下左右中为1的格子 题解代码 func maxAr…

【通信协议】一文学会异步、同步、串行、并行、单工、半双工、全双工(一)

通信方式详解:异步、同步、串行、并行、单工、半双工、全双工 引言一、通信方式分类概述二、串行通信与并行通信串行通信 (Serial Communication)并行通信 (Parallel Communication)串行与并行通信对比表 三、全双工、半双工、单工通信单工通信 (Simplex Communicat…

LLM+知识图谱新工具! iText2KG:使用大型语言模型构建增量知识图谱

iText2KG是一个基于大型语言模型的增量知识图谱构建工具,通过从文本文档中提取实体和关系来逐步构建知识图谱。该工具具有零样本学习能力,能够在无需特定训练的情况下,在多个领域中进行知识提取。它包括文档提炼、实体提取和关系提取模块&…

最新版IntelliJ IDEA 2024.2.3 创建SpringBoot项目(包含各种依赖的选择和功能)

创建SpringBoot项目 1 . 打开IDEA 选择新建项目 2. 基础项目创建 在顶端几个选项可以选择创建基本的java项目 填写项目名称,文件位置,选择构建工具 3. 下方选择springboot 选择构建的方式 三种方式虽然不同但是,基本功能都一致, Gradle-Groovy 是指使用 Groovy 语言编写…

Redis安装RedisBloom插件

Redis安装RedisBloom插件 1. 下载RedisBloom2. 安装RedisBloom3. Redis 安装RedisBloom4. 验证是否安装成功5. 其他安装方法5.1 使用 Docker 安装 RedisBloom5.2 通过 RedisStack 安装 RedisBloom 是一个 Redis 模块,它提供了一种高效的方式来存储和检索大数据集中的…