C语言—单链表

news2024/11/29 9:53:41

目录

一、链表的概念及结构

二、单链表实现

(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.coloradmin.cn/o/2192006.html

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

相关文章

Anaconda的安装与环境设置

文章目录 一、Anaconda介绍二、Anaconda环境搭建1. 下载Anaconda(1)官网下载(2)清华大学镜像 2. 安装Anaconda3.配置环境变量4.检验conda是否安装成功5.更改镜像源6.若菜单栏没有conda prompt 三、虚拟环境1.创建、查看、删除虚拟环境2.激活、退出虚拟环境 四、CUDA、Pytorch、…

基于SpringBoot+Vue的酒店客房管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…

qemu-system-aarch64开启user用户模式网络连接

一、问题 在使用qemu构建arm64的虚拟机时,虚拟机没有网络,桥接方式相对麻烦,我只是需要联网更新即可。与宿主机的通信我使用共享文件夹即可满足要求。 使用指令启动虚拟机时,网络部分的参数为 -net user,hostfwdtcp::10022-:22 …

白板2-数学基础

高斯分布1-极大似然估计 高斯分布2-极大似然估计-无偏&有偏 高斯分布3-从概率密度角度高斯分布4-局限性高斯分布5-边缘概率及条件概率高斯分布6-求联合概率分布

八大排序--01冒泡排序

假设有一组数据 arr[]{2,0,3,4,5,7} 方法:开辟两个指针,指向如图,前后两两进行比较,大数据向后冒泡传递,小数据换到前面。 一次冒泡后,数组中最大…

codetop标签动态规划大全C++讲解(四)!!动态规划刷穿地心!!学吐了家人们o(╥﹏╥)o

一天复习一篇,个人学习记录 1.最大子数组和2.最长的斐波那契子序列的长度3.最大正方形4.最长有效括号5.乘积最大子数组6.可被三整除的最大和7.回文子串数目8.最长回文子序列9.最长回文子串 1.最大子数组和 给你一个整数数组 nums ,请你找出一个具有最大…

SpringBoot在线教育平台:设计与实现的深度解析

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…

【JAVA开源】基于Vue和SpringBoot的教学资源库系统

本文项目编号 T 067 ,文末自助获取源码 \color{red}{T067,文末自助获取源码} T067,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计5.4.1 管…

654、最大二叉树

1、题目描述 . - 力扣(LeetCode) 其实就是给定了一个所谓"最大二叉树"的规则,让我们去构建二叉树。 以 nums [3,2,1,6,0,5] 为例,规则如下: (1)找出其中的最大值6将其作为根节点,6前面的是左子…

自动驾驶系列—线控系统:驱动自动驾驶的核心技术解读与应用指南

🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…

工具 | 红队大佬亲测5款推荐的Burpsuite插件

*免责声明:* *本文章仅用于信息安全技术分享,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作…

基于SpringBoot+Vue+MySQL的校园二手物品交易系统

系统展示 用户前台界面 管理员后台界面 系统背景 校园二手物品交易系统开发的背景与重要性随着高等教育的蓬勃发展,大学生群体的规模持续扩大,随之而来的是物品更新换代速度的显著加快。学生们在追求新潮、高品质生活的同时,往往会产生大量闲…

微信步数C++

题目: 样例解释: 【样例 #1 解释】 从 (1,1) 出发将走 2 步,从 (1,2) 出发将走 4 步,从 (1,3) 出发将走 4 步。 从 (2,1) 出发将走 2 步,从 (2,2) 出发将走 3 步,从 (2,3) 出发将走 3 步。 从 (3,1) 出发将…

llm接口高可用工程实践(尽快关注我,以后这些文章将只对粉丝开放)

上一节课程链接:中文llama3仿openai api实战-CSDN博客 ,本文是在上一节基础上继续操作 课程介绍 本文基于Chinese-LLaMA-Alpaca-3(https://github.com/ymcui/Chinese-LLaMA-Alpaca-3)项目,介绍如何通过搭建2个llama3…

生信初学者教程(二十五):验证候选特征

文章目录 介绍加载R包导入数据函数重要特征的表达ROC分析汇总筛选结果输出结果总结介绍 在成功识别出核心特征之后,为了验证这些特征的有效性和可靠性,我们在发现数据集和验证数据集上进行了进一步的评估。这一步骤旨在确保这些特征在不同数据集上的表达值具有一致性,并验证…

大模型时代下小模型知多少?从模型结构、预训练数据到运行时成本分析总结

今天,我们来谈谈小模型。《Small Language Models综述,Small Language Models: Survey, Measurements, and Insights》:https://arxiv.org/pdf/2409.15790这个工作,会有一些启发。 本文主要介绍三个话题,一个是小模型…

【笔记】信度检验

一、信度 信度是指测量结果的一致性和稳定性。 1.一致性(Consistency) 一致性指的是测量工具内部各个部分或项目之间的协调一致程度。高一致性意味着测量工具的不同部分都在测量同一个概念或特质。 例子:智力测试 假设我们有一个包含100…

成为AI产品经理,应该具备哪些条件?

开篇勘误标题:未来不会有AI产品经理这个岗位,就像没有移动产品经理一样。 如果你是个产品经理,但是不懂移动端的产品交互和设计,那你就只能在自己的头衔前面加上一个“PC”:PC产品经理,代表你的细分或者不…

在线Html到Markdown转换器

具体请前往:在线Html转Markdown

6个最受欢迎的大模型本地运行工具

运行大型语言模型 (LLM)(如 ChatGPT 和 Claude)通常涉及将数据发送到 OpenAI 和其他 AI 模型提供商管理的服务器。虽然这些服务是安全的,但一些企业更愿意将数据完全离线,以保护更大的隐私。 本文介绍了开发人员可以用来在本地运…