数据结构——链表(单链表)

news2025/1/25 4:38:37

大家好,又是我(小锋),今天给大家带了一个比较有挑战的章节(链表),但是不用担心,小锋会陪大家一起度过。

顺序表的思考与问题

1. 中间/头部的插入删除,时间复杂度为O(N)
我们在进行一些插入删除操作时我们会先内存覆盖然后再进行操作,覆盖内存的时间复杂度为O(N)。
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
当我们进行扩容时我们是不是要用realloc函数,而realloc函数再扩容时分为异地扩容,和原地扩容,当我们异地扩容时又有一系列的操作这又加大了消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到
200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
我们增加容量后可能用不完,这就会造成空间的浪费,以及顺序表的空间是连续的我们要释放就必须全部释放。
那我们今天讲到链表就不会出现上面的问题,但也不是说链表就没有缺点,不论是顺序表还是链表都有优缺点,重点在于我们怎么运用。

链表

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
我们这节主要讲解单链表
这个就是链表的逻辑结构了,一个节点中有指向下一个节点的指针,这样走下去最后一个节点中放的是空指针NULL,像链条一样一环扣一环。
我给大家画一个物理结构方便大家理解
我们可以形象看出链表的基本结构
那我们来创建一个链表
我们先来实现一个打印链表内容的函数

单链表打印

//链表输出
 void LBexport(LBbiao* att) {
	 assert(att);
	 LBbiao* ps = att;
	 while (ps!= NULL) {
		 printf("%d->", ps->SZ);
		 ps = ps->next;
	 }
	 printf("NULL\n");
 }

这里大家可以画图一步一步的理一下理解会更加深刻主要注意循环的判断条件。

我们下面来实现一个插入函数

单链表头插数据

我们想一想在链表的头部插入数据我们只需要开辟一个链表空间mon然后另它的next指向当前链表的表头的地址,并将mon的地址交给表头的指针是不是就可以插入数据并且将链表连接起来了?

大家可以看看大致原理如图
我们还要注意要进行断言防止出现错误
//链表头插数据
 void LBcutin(LBbiao** att,CMMlet n) {
	 assert(att);
	 LBbiao* pt = LBopen();
	 if (*att == NULL) {
		 *att = pt;
	 }
	 else {
		 pt->next = *att;
		 *att = pt;
	 }
	 pt->SZ = n;
 }
这里开辟一个新的链表空间我们还会用到所有我们用一个函数来实现
//开辟链表
 LBbiao* LBopen() {
	 LBbiao* mon = (LBbiao*)malloc(sizeof(CMMlet) + sizeof(LBbiao*));
	 if (mon == NULL) {
		 printf("开辟失败:%s",strerror(errno));
		 return NULL;
	 }
	 mon->next = NULL;
	 mon->SZ = 0;
	 return mon;
 }
我们这里可以用老方法,分装一个函数来验证

单链表头删数据

还是我们用图来演示

//链表头删数据
 void LBdelete(LBbiao**att) {
	 assert(*att != NULL);
	 assert(att != NULL);//断言
	 LBbiao* ps = *att;
	 *att = ps->next;
	 ps->next = NULL;
	 free(ps);
 }

我们验证试试

单链表尾删数据

我们还是画图演示原理

我们可以看见尾删数据直接将倒数第二个节点的next为空就行了,当然我们还要断言一些情况

并且我们还要分情况当链表只有一个节点时该怎么删。

//链表尾删数据
 void LBweishan(LBbiao*att) {
	 assert(att != NULL);//断言
	 LBbiao* ps = att;
	 while (ps->next->next != NULL) {
		 ps = ps->next;
	 }
	 free(ps->next);
	 ps->next = NULL;
 }

单链表尾插数据

老规矩画图理解

我们通过图可以看出尾插数据要开辟一个新的节点然后让原链表的最后一个节点的next指向新开辟出来的节点并将新开辟的节点的next为空。同样我们要分情况讨论

//链表尾插数据
 void LBweicha(LBbiao** att,CMMlet n) {
	 LBbiao* ps = *att;
	 LBbiao* pt = LBopen();
	 if (ps == NULL) {
		 *att = pt;
	 }
	 else {
		 while (ps->next != NULL) {
			 ps = ps->next;
		 }
		 ps->next = pt;
	 }
	 pt->SZ = n;
 }

单链表查找

我们可以直接遍历链表如果遇到我们就返回该数据的节点地址,如果没找到我们返回NULL。

//链表查找
 LBbiao* LBchazhao(LBbiao* att, CMMlet n) {
	 assert(att);
	 LBbiao* ps = att;
	 while (ps != NULL) {
		 if (ps->SZ == n) {
			 return ps;
			 break;
		 }
		 ps = ps->next;
	 }
	 return NULL;
 }

我们可以测试一下

单链表在pos位置之后插入x

与头插类似我们主要注意的是要不要分情况讨论,以及断言的内容,具体插入的过程我们大家可以自己画图试试

接下来我们来实现一下这个函数

//链表在pos位置后插入x

 void LBporcr(LBbiao* pos, CMMlet x) {
	 assert(pos);
	 LBbiao* ps = pos;
	 LBbiao* pt = LBopen();
	 if (pos->next == NULL) {//相当于尾插数据
		 LBweicha(&pos, x);
	 }
	 else {
		//一般情况
		 pt->next=ps->next;
		 ps->next = pt;
		 pt->SZ = x;
	 }

 }

验证试试

单链表删除pos位置之后的值

我们还是用图说话

分析后我们发现我们只需要将pos位置的next指向pos->next->next就ok了,最后还要注意一下特殊情况以及断言

//链表在pos位置后删除x
 void LBpossc(LBbiao* pos) {
	 assert(pos&&pos->next!=NULL);
	 if (pos->next->next == NULL) {//相当于尾删
		 free(pos->next);
		 pos->next = NULL;
	 }
	 else {
		 LBbiao* ps = pos->next;
		 pos->next = pos->next->next;
		 ps->next = NULL;
		 free(ps->next);

	 }
 }

我们可以测试一下

链表空间的释放

同学们,因为链表是环环相扣的我们如果将一个空间节点的空间释放那么我们就无法找到下一块空间了所有我们这里应该设置两个指针一个指向要释放空间的节点一个指向下一个节点,

//链表释放空间
 void LBshifang(LBbiao** att) {
	 assert(att);
	 assert(*att);
	 LBbiao* ps = *att;
	 LBbiao* pt = ps->next;
	 while (ps) {
		 ps->next = NULL;
		 free(ps);
		 ps = pt;
		 if (pt!=NULL) {
			 pt = pt->next;
		 }
	 }
	 *att = NULL;
 }

​​​​​​​

我们测试一下

我们看是不是最后链表空间都被释放了?

同学们至此我们的单链表就讲解完了是不是畅快淋漓呢?

这是我们本节的所有代码大家可以自己试试

# define _CRT_SECURE_NO_WARNINGS 1
# include<stdio.h>
# include<assert.h>
# include<string.h>
# include<errno.h>
# include<stdlib.h>



//类型重命名
//方便后期修改
typedef struct LBbiao LBbiao;
typedef int CMMlet;
//链表创建
 struct LBbiao{
	CMMlet SZ;//链表存储的数据
	struct LBbiao* next;//指向下一个节点

 };


 //开辟链表
 LBbiao* LBopen() {
	 LBbiao* mon = (LBbiao*)malloc(sizeof(CMMlet) + sizeof(LBbiao*));
	 if (mon == NULL) {
		 printf("开辟失败:%s",strerror(errno));
		 return NULL;
	 }
	 mon->next = NULL;
	 mon->SZ = 0;
	 return mon;
 }

 //链表输出
 void LBexport(LBbiao* att) {
	 assert(att);
	 LBbiao* ps = att;
	 while (ps!= NULL) {
		 printf("%d->", ps->SZ);
		 ps = ps->next;
	 }
	 printf("NULL\n");
 }

 //链表头插数据
 void LBcutin(LBbiao** att,CMMlet n) {
	 assert(att);
	 LBbiao* pt = LBopen();
	 if (*att == NULL) {
		 *att = pt;
	 }
	 else {
		 pt->next = *att;
		 *att = pt;
	 }
	 pt->SZ = n;
 }

 //链表头删数据
 void LBdelete(LBbiao**att) {
	 assert(*att != NULL);
	 assert(att != NULL);//断言
	 LBbiao* ps = *att;
	 *att = ps->next;
	 ps->next = NULL;
	 free(ps);
 }

 //链表尾删数据
 void LBweishan(LBbiao**att) {
	 assert(att != NULL);//断言
	 assert(*att != NULL);

	 LBbiao* ps = *att;
	 if (ps->next == NULL) {
		 *att = NULL;
		 free(ps);
	 }
	 else {
		 while (ps->next->next != NULL) {
			 ps = ps->next;
		 }
		 free(ps->next);
		 ps->next = NULL;
	 }
 }

 //链表尾插数据
 void LBweicha(LBbiao** att,CMMlet n) {
	 LBbiao* ps = *att;
	 LBbiao* pt = LBopen();
	 if (ps == NULL) {
		 *att = pt;
	 }
	 else {
		 while (ps->next != NULL) {
			 ps = ps->next;
		 }
		 ps->next = pt;
	 }
	 pt->SZ = n;
 }
 //链表查找
 LBbiao* LBchazhao(LBbiao* att, CMMlet n) {
	 assert(att);
	 LBbiao* ps = att;
	 while (ps != NULL) {
		 if (ps->SZ == n) {
			 return ps;
			 break;
		 }
		 ps = ps->next;
	 }
	 return NULL;
 }




 //链表在pos位置后插入x

 void LBporcr(LBbiao* pos, CMMlet x) {
	 assert(pos);
	 LBbiao* ps = pos;
	 LBbiao* pt = LBopen();
	 if (pos->next == NULL) {//相当于尾插数据
		 LBweicha(&pos, x);
	 }
	 else {
		//一般情况
		 pt->next=ps->next;
		 ps->next = pt;
		 pt->SZ = x;
	 }

 }


 //链表在pos位置后删除x
 void LBpossc(LBbiao* pos) {
	 assert(pos&&pos->next!=NULL);
	 if (pos->next->next == NULL) {//相当于尾删
		 free(pos->next);
		 pos->next = NULL;
	 }
	 else {
		 LBbiao* ps = pos->next;
		 pos->next = pos->next->next;
		 ps->next = NULL;
		 free(ps->next);

	 }
 }

 //链表释放空间
 void LBshifang(LBbiao** att) {
	 assert(att);
	 assert(*att);
	 LBbiao* ps = *att;
	 LBbiao* pt = ps->next;
	 while (ps) {
		 ps->next = NULL;
		 free(ps);
		 ps = pt;
		 if (pt!=NULL) {
			 pt = pt->next;
		 }
	 }
	 *att = NULL;
 }



 //测试链表1
 void ceshi1() {
	 LBbiao* arr = NULL;
	 LBweicha(&arr, 1);
	 LBweicha(&arr, 2);
	 LBweicha(&arr, 3);
	 LBweicha(&arr, 4);
	 LBshifang(&arr);


 }

 int main() {
	 ceshi1();


	 return 0;
 }

  以上就是全部内容了,如果有错误或者不足的地方欢迎大家给予建议。 

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

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

相关文章

rhcsa复习4

文件权限 文件的权限针对三类对象进行定义 owner 属主&#xff0c;缩写 u group 属组&#xff0c;缩写 g other 其他&#xff0c;缩写 o 每个文件针对每类访问者定义了三种主要权限 r &#xff1a; Read 读 - 文本文件 cat tac more less head tail paste d ls -l 列…

数字范围按位与

题目链接 数字范围按位与 题目描述 注意点 0 < left < right < 2^31 - 1包含 left 、right 端点 解答思路 返回区间内所有数字按位与的结果&#xff0c;所以区间内所有数字在某一位的值相同&#xff0c;则结果该位数字为该值&#xff0c;否则该位数字为0&#xf…

【漏洞复现】XETUX 系统 dynamiccontent 接口处存在远程代码执行漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

xftp突然无法连接虚拟机

问题描述 使用xftp连接虚拟机的时候一直显示 连接xxx.xxx.xx.xx失败 问题原因查找 首先打开本地cmd命令提示符 ping 你的虚拟机ip地址 我的是 ping 192.168.xx.xx 显示请求超时 解决方案&#xff1a; 点击打开更改适配器选项 右键vmnet 8——属性 如图前四个选项必选 单…

docker环境配置过程中的常见问题

1、pull镜像问题 docker pull jenkins/jenkins:lts Using default tag: latest Trying to pull repository docker.io/library/centos ... Get https://registry-1.docker.io/v2/library/centos/manifests/latest: Get https://auth.docker.io/token?scoperepository%3Alibr…

液体和固体介质的电气特性

本篇为本科课程《高电压工程基础》的笔记。 液体和固体介质广泛应用于高压电气设备内&#xff0c;作为设备的内绝缘。描述电介质的电气特性的四大参数是介电常数 ε \varepsilon ε、电导率 γ \gamma γ、介质损耗角正切 tan ⁡ δ \tan\delta tanδ和击穿场强 E b E_b Eb​。…

【智能家居项目】RT-Thread版本——DHT11获取温湿度 | MQTT上传到服务器 | 服务器控制外设

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 这篇文章中&#xff0c;本喵将使用RT-Thread Studio来实现这个智能家居的项目&#xff0c;最终…

词令外卖节红包天天神券每天领取直达入口

词令外卖节红包天天领直达入口 1、打开「词令」关键词口令直达微信小程序&#xff1b; 2、输入词令「外卖红包88」关键词直达口令&#xff1b; 3、搜索直达进入外卖红包天天领入口&#xff0c;即可成功领取外卖节红包和天天神券点外卖可享受券后价优惠&#xff1b; *温馨提醒&…

故障诊断模型 | 基于多信号融合和改进的深度卷积生成对抗网络的不平衡数据故障诊断方法

文章目录 文章概述模型描述参考资料文章概述 本文提出了一种解决数据不平衡问题并提高诊断准确性的诊断方法。首先,通过小波变换处理来自多个传感器的信号,以增强数据特征,然后通过池化和拼接操作对其进行压缩和融合。随后,构建改进的对抗网络来生成新的样本进行数据增强。…

C#预处理器指令(巨细版)

文章目录 一、预处理器指令的基本概念二、预处理器指令的基本规则三、C# 预处理器指令详解3.1 #define 和 #undef3.2 #if、#else、#elif 和 #endif3.3 #line3.4 #error 和 #warning3.5 #region 和 #endregion 四、高级应用&#xff1a;预处理器指令的最佳实践4.1 条件编译的最佳…

.Net 知识杂记

记录平日中琐碎的.net 知识点。不定期更新 目标框架名称(TFM) 我们创建C#应用程序时&#xff0c;在项目的工程文件(*.csproj)中都有targetFramework标签&#xff0c;以表示项目使用的目标框架 各种版本的TFM .NET Framework .NET Standard .NET5 及更高版本 UMP等 参考文档&a…

公司服务器被.rmallox攻击了如何挽救数据?

公司服务器被.rmallox攻击了如何挽救数据&#xff1f; .rmallox这种病毒与之前的勒索病毒变种有何不同&#xff1f;它有哪些新的特点或功能&#xff1f; .rmallox勒索病毒与之前的勒索病毒变种相比&#xff0c;具有一些新的特点和功能。这种病毒主要利用加密技术来威胁用户&am…

Ubuntu20.04LTS+uhd3.15+gnuradio3.8.1源码编译及安装

文章目录 前言一、卸载本地 gnuradio二、安装 UHD 驱动三、编译及安装 gnuradio四、验证 前言 本地 Ubuntu 环境的 gnuradio 是按照官方指导使用 ppa 的方式安装 uhd 和 gnuradio 的&#xff0c;也是最方便的方法&#xff0c;但是存在着一个问题&#xff0c;就是我无法修改底层…

vue3使用vuedraggable实现拖拽(有过渡)

1. 安装与使用 vue中vuedraggable安装&#xff1a; pnpm i -S vuedraggablenext或者 yarn add vuedraggablenext注意&#xff1a;vue2和vue3安装的是不同版本的vuedraggable&#xff0c;写法上也会有一些区别。 比如在vue3中使用拖拽&#xff0c;要以插槽的方式&#xff0c;…

Unity DOTS中的baking(四)blob assets

Unity DOTS中的baking&#xff08;四&#xff09;blob assets blob assets表示不可变的二进制数据&#xff0c;在运行时也不会发生更改。由于blob assets是只读的&#xff0c;这意味着可以安全地并行访问它们。此外&#xff0c;blob assets仅限于使用非托管类型&#xff0c;这意…

从TCP/IP协议到socket编程详解

​ 我的所有学习笔记&#xff1a;https://github.com/Dusongg/StudyNotes⭐⭐⭐ ​ 文章目录 1 网络基础知识1.1 查看网络信息1.2 认识端口号1.3 UDP1.4 TCP1.4.1 确认应答机制1.4.2 TCP三次握手/四次挥手为什么是三次握手为什么是四次挥手listen 的第二个参数 backlog—— 全…

ES6中的Set集合

Set集合 ES6 提供了新的数据结构Set(集合)。 它类似于数组&#xff0c;但成员的值都是唯一的集合实现了 iterator 接口&#xff0c;所以可以使用「扩展运算符」和[for…of…」进行遍历集合的属性和方法 集合的属性和方法&#xff1a; 1&#xff09;size&#xff0c;返回集合的元…

应用分层(三层架构)

1、 2、它比MVC更合理&#xff0c;MVC的任务分配不太均匀&#xff0c;model处理的问题过多&#xff0c;进一步改进成三层架构更为合理 3、 4、两者共同点&#xff1a;解耦 5、高内聚低耦合 &#xff08;1&#xff09;模块内&#xff1a;关系尽量紧密 &#xff08;2&#xf…

HTTP 与 HTTPS 的区别

基本概念 HTTP&#xff08;HyperText Transfer Protocol&#xff1a;超文本传输协议&#xff09;是一种应用层协议&#xff0c;主要用于在网络上进行信息的传递&#xff0c;特别是用于Web浏览器和服务器之间的通信。 它使用明文方式发送数据&#xff0c;这意味着传输的内容可…

CTK插件框架学习-插件注册调用(03)

CTK插件框架学习-新建插件(02)https://mp.csdn.net/mp_blog/creation/editor/136923735 一、CTK插件组成 接口类&#xff1a;对外暴露的接口&#xff0c;供其他插件调用实现类&#xff1a;实现接口内的方法激活类&#xff1a;负责将插件注册到CTK框架中 二、接口、插件、服务…