【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)

请添加图片描述

🔥引言

本篇将深入解析单链表:探索底层逻辑,理解底层是如何实现并了解该接口实现的优缺点,以便于我们在编写程序灵活地使用该数据结构。

请添加图片描述
Alt

🌈个人主页:是店小二呀
🌈C语言笔记专栏:C语言笔记
🌈C++笔记专栏: C++笔记
🌈初阶数据结构笔记专栏: 初阶数据结构笔记

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅
请添加图片描述

文章目录

  • 一、链表的概念
  • 二、链表的分类
  • 四、实现无头单向非循环链表的相关接口(SLTlist.h)
  • 五、知识铺垫
  • 六、正式开始模拟实现单链表
    • 6.1 创建链表中的节点
    • 6.2 单链表的插入节点
      • 6.2.1 单链表的尾插
      • 6.2.2 单链表的头插
    • 6.3 单链表的删除
      • 6.3.1 单链表的尾删
      • 6.3.2 单链表的头删
    • 6.4 查找单链表中数据
    • 6.5 关于单链表的任意位置插入和删除
      • 6.5.1 单链表的pos指定前插入
      • 6.5.2 单链表的删除pos当前结点
      • 6.5.3 单链表的pos之后插入
      • 6.5.4 单链表的pos之后删除
    • 6.6 单链表的打印
  • 七、顺序表和链表的区别

一、链表的概念

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

二、链表的分类

在这里插入图片描述

我们重点需要关注以下两个链表:

1.无头单向非循环链表

结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2.带头双向循环链表

结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。虽然结构复杂,但是使用代码实现,以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了

链表是通过一个个结点链接起来的数据结构,多个结点链接形成下列结构(箭头是不存在,是为了方便理解)
在这里插入图片描述

下列图片会简化结点间的链接过程:

在这里插入图片描述

注意】:

  1. 从图上可以看出,链式结构在逻辑上是连续的,但是在物理上不一定连续

  2. 现实中的节点一般都是从堆上申请出来

  3. 从堆上申请的空间。是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

四、实现无头单向非循环链表的相关接口(SLTlist.h)

在这里插入图片描述

五、知识铺垫

1.实现部分接口需要通过二级指针接受实参

原因在于我们需要可以修改实参,而是实参为一级指针时(同样是传递地址),需要使用二级指针进行接受,否则获得临时拷贝,不会影响到实参。修改实参的情况,比如一开始为空,在插入时需将头指针存储在有效结点的的地址上,需要改变实参的值

2.单链表的初始化

这里实现链表,没有必要进行初始化,初始化对于一开始就要开辟的空间有初始化的需求,表是多个节点通过地址链接在一起,那么只需要开辟新节点的时候,初始化下就行了(有哨兵位需要初始化)

3.二级指针断言

二级指针存放的是头节点的地址,头节点的地址为空,那么还有什么意义呢?

当我们有所了解链表的结构,接下来是实现链表的相关接口,比如增删查改

六、正式开始模拟实现单链表

6.1 创建链表中的节点

在插入中需要先创建一块结点空间,再通过上一个结点通过当前结点的地址指向当前结点的位置。这是因为结点是通过地址访问的,结点里面存储着下一个节点的地址,理解为当前结点(通过下一个结点地址)指向下一个结点

SLNode* CreateNode(SLNDataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode==NULL)
	{
		perror("malloc fail!!!");
		 return (-1);
	}
	newnode->next = NULL;
	newnode->val = x;
	return newnode;
}

这里需要注意的是:申请到的空间交给什么类型去维护,为结点(结构体)申请空间,就需要交给结构体指针维护,同时需要注意开辟空间可能会失败,比如开辟空间多大,无法提供空间。对新结点设置了指向下一个结点为空

6.2 单链表的插入节点

插入分为三类:头插\尾插\任意位置插入(其中任意位置插入,在实现查找功能先放着)

6.2.1 单链表的尾插

void SLTPushBack(SLNode** phead,SLNDataType x)
{
	assert(phead);
	SLNode* newnode = CreateNode(x);//值已经有了,创建一个新节点
	if (*phead == NULL)//这里需要二级指针去改变了,外的头了
	{
		*phead = newnode;
	}
	else
	{
		//找尾
		SLNode* cur =*phead;//拷贝一份
		while (cur->next != NULL)
		{
			cur = cur->next;
		}
		cur->next = newnode;//newnode已经搞下一次是空了	
	}	
}

这里需要注意的是:while语句cur需要到达尾,再进行尾插的操作。同时需要考虑到特殊情况,这里我们通过if判断语句对于* pphead为空的情况,将*pphead存储在第一个结点地址。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

6.2.2 单链表的头插

void SLTPushFront(SLNode** pphead, SLNDataType x)
{
	assert(pphead);
	SLNode* newnode = CreateNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLNode* cur = *pphead;
		*pphead = newnode;
		newnode->next = cur;
	}
}

这里需要注意的是:将*pphead移动到新节点的位置,再 * pphead指向cur(在原来的头节点位置)。同样的需要考虑到特殊情况,这里使用if判断语句对于* pphead为空的情况,将*pphead设为存储第一个结点地址。
在这里插入图片描述

6.3 单链表的删除

删除分为三类:头删\尾删\任意位置删除(其中任意位置删除,在实现查找功能先放着)

提前说明:空链表无法进行删除数据,需要在删除操作之前进行断言检查assert(*pphead)

6.3.1 单链表的尾删

void SLTPopBack(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);//空的时候
	//一个节点和多个节点
	//这里不创建一个cur变量,当只有一个节点的时候,直接pphead
	SLNode* cur = *pphead;
	if (cur->next == NULL)
	{
		*pphead = NULL;
		free(cur);
		cur = NULL;
	}
	else
	{
		while (cur->next->next!= NULL)
		{
			cur = cur->next;//上一个节点
		}
		free(cur->next);
		cur->next = NULL;
	}
}

这里需要注意的是:删除需要分为两种情况存在一个节点和多个节点的处理。需要利用while循环找到删除节点的上一个节点,将上一个节点指向空,最后不要忘记free(cur->next),释放当前节点空间。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

6.3.2 单链表的头删

void SLTPopFront(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLNode* cur = *pphead;
	if (cur->next == NULL)
	{
		*pphead = NULL;
		free(cur);
		cur = NULL;
	}
	else
	{
		*pphead = cur->next;
		free(cur);
		cur = NULL;
	}
}

这里需要注意的是:删除需要分为两种情况存在一个节点和多个节点的处理。cur保存当头节点位置,*pphead移动到下一个节点的位置,再free(cur)
在这里插入图片描述

6.4 查找单链表中数据

SLNode* SLTFind(SLNode* pphead, SLNDataType x)
{
	SLNode* cur = pphead;
	while (cur!=NULL)
	{
		if (cur->val == x)
			return cur;
		else
			cur = cur->next;
	}
	return NULL;
}

这里需要注意的是:遍历链表,找到返回当前节点,没有找到继续向下遍历

6.5 关于单链表的任意位置插入和删除

6.5.1 单链表的pos指定前插入

void SLTInsert(SLNode** pphead,SLNode *pos,SLNDataType x)//pos指定之前插入
{
	assert(pphead);
	assert(*pphead);
	if (pos == NULL)//没有节点
	{
		SLTPushFront(pphead, x);
	}
	if (*pphead == pos)//一个节点
	{
		SLTPushFront(pphead, x);
	}
	else//多个节点
	{
		SLNode* newnode = CreateNode(x);
		SLNode* cur = *pphead;
		while (cur->next != pos)//上面避免了pos等于cur
		{
			cur = cur->next;
		}
		cur->next = newnode;
		newnode->next = pos;
	}
}

这里需要注意的是:需要分为三种情况,空节点,一个节点,多个节点就行处理。空节点调用尾插或者头插都可以,一个节点(在pos前插入)那么可以调用头插
在这里插入图片描述

6.5.2 单链表的删除pos当前结点

void SLTEeara(SLNode** pphead, SLNode* pos)
{
	assert(pphead);//防止用户传个空指针
	assert(*pphead);
	assert(pos);
	SLNode* next = pos->next;
	SLNode* cur = *pphead;

	if (cur==pos)
	*pphead = NULL;
        free(cur);
		cur = NULL;
	else
	{
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		cur->next = next;
		free(pos);
		pos = NULL;
	}
}

这里需要注意的是:分为两种情况,存在一个节点和多个节点的处理。如果使用三个变量,需要使用到的地址不会丢失,就不需要担心先后顺序问题。结点是一块块的独立空间,其链接方式也是较灵活的,这里跟上面方法是类似的。
在这里插入图片描述

如果不想在pos之前插入\删除,可以改动逻辑在pos之后进行插入、删除。

6.5.3 单链表的pos之后插入

void SLTInsertAfter(SLNode **pphead,SLNode* pos, SLNDataType x)
{
	assert(pphead);
	assert(*pphead);
	if (pos == NULL)//没有节点
	{
		SLTPushFront(pphead, x);
	}
	if (*pphead == pos)//一个节点
	{
		SLTPushBack(pphead, x);
	}
	else//多个节点
	{
		SLNode* newnode = CreateNode(x);
		SLNode* back = pos->next;
		newnode->next = back;
		pos->next = newnode;
	}
}

6.5.4 单链表的pos之后删除

void SLTEearaAfter(SLNode **pphead,SLNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	SLNode* cur = *pphead;
	SLNode* back = pos->next;
	if (cur == pos)//只有一个结点
		SLTPopBack(pphead);
	if(back->next==NULL)
	{
		free(back);
		back = NULL;
		pos->next = NULL;
	}
	else
	{
		pos->next = back->next;
		free(back);
		back = NULL;
	}
}

上面的两个接口实现过程跟SLTInsertSLTEeara实现类似的,看看代码就能理解

在完成了单链表的核心接口,我们需要继续完善剩下的接口,使实现的单链表功能更加丰富起来。

6.6 单链表的打印

void SLTPrint(SLNode** pphead)//二级指针改变外的结构体指针类型
{
	assert(pphead);
	SLNode* cur = *pphead;
	while (cur!= NULL)
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}

这里需要注意的是:当cur==NULL时,没有进去循环,需要额外打印NULL,最后不要忘记单链表的销毁

void SLTDestroy(SLNode** pphead)
{
	assert(pphead);
	SLNode* cur = *pphead;
	while (cur)
	{
		SLNode* next = cur->next;
		free(cur);
		//这里cur不要赋空,还需要使用的
		cur = next;
}
	*pphead = NULL;
}

这里需要注意的是:链表是通过多个节点链接而成的,同时也是一块块独立空间,通过cur去访问每一个空间和释放每一块空间。其中free指针跟指针变身是没有关系的,释放的是指针所指向的那一块动态空间

七、顺序表和链表的区别

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定 连续
随机访问支持O(1)不支持:O(N)
任意位置插入或者删除 元素可能需要搬移元素,效率低 O(N)只需修改指针指向
插入动态顺序表,空间不够时需要 扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁
缓存利用率

不管是哪一种数据结构都有他的优点和缺点,对此在使用数据结构中应该知道它的优缺点是什么,加以合理地利用解决实际中的问题。


请添加图片描述

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二初阶数据结构笔记,希望对你在学习初阶数据结构中有所帮助!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【软件测试入门】软件测试那些事

在日常生活中,我们早已习惯于各类软件带来的便捷与效率,从手机里的应用程序到电脑上的办公软件,它们无声地编织着现代社会的运作网络。然而,每一款流畅运行、体验优良的软件背后,都离不开一个关键环节——软件测试。《…

同三维T80004EH-N HDMI高清NDI编码器

1路HDMI 1路3.5音频输入,支持NDI 产品简介: 同三维T80004EH-N 高清HDMI编码器是专业的NDI高清音视频编码产品,该产品支持1路高清HDMI音视频采集功能,1路3.5MM独立音频接口采集功能。编码输出双码流H.265/H.264格式,音频MP3/AAC格…

SRM供应商管理系统建设方案及源码实现(方案+源码)

1. 供应商管理 2. 采购需求管理 3. 采购寻源管理 4. 采购合同管理 5. 采购订单管理 6. 采购协同管理 7. 外部商城采购管理 8. 报表查询管理 9. 系统管理 10. 集成管理 资料获取:本文末个人名片。

免费分享一套SpringBoot+Vue房地产销售管理系统【论文+源码+SQL脚本+PPT+开题报告】,帅呆了~~

大家好,我是java1234_小锋老师,看到一个不错的SpringBootVue房地产销售管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue房地产销售管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue房地产销售管理系统 Java毕业设计…

【解决方案】数据采集工作站数据传不上去?

数据采集工作站扮演着至关重要的角色,它们负责收集、处理和传输各种传感器和设备的数据。然而,有时会遇到数据传输失败的问题。本文将详细探讨数据采集工作站数据传不上去的可能原因及其解决方案。(更多了解采集器设备可前往苏州稳联&#xf…

【Android】安卓开发的前景

人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…

【elementui源码解析】如何实现自动渲染md文档-第二篇

目录 1.概要 2.引用文件 1)components.json 2)json-template/string 3)os.EOL 3.变量定义 4.模版填充 5.MAIN_TEMPLATE填充 6.src下的index.js文件 1)install 2)export 7.总结 所有章节: 【el…

【Spring】1. Maven项目管理

📚博客主页:爱敲代码的小杨. ✨专栏:《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞👍🏻收藏⭐评论✍🏻,您的三连就是我持续更…

电子货架标签:零售业的未来趋势

随着科技的飞速发展,传统零售业正经历着一场前所未有的变革。电子货架标签作为零售业的一项创新技术,正在以惊人的速度改变着消费者的购物体验,同时也为零售商带来了巨大的商业机遇。本文将探讨电子货架标签的发展现状、优势以及对零售业未来…

【可控图像生成系列论文(一)】MimicBrush 港大、阿里、蚂蚁集团合作论文解读

背景:考虑到用户的不同需求,图像编辑是一项实用而富有挑战性的任务,其中最困难的部分之一是准确描述编辑后的图像应该是什么样子。 创新点:在本文作者提出了一种新的编辑形式,称为模仿编辑,以帮助用户更方…

post为什么会发送两次请求详解

文章目录 导文跨域请求的预检复杂请求的定义服务器响应预检请求总结 导文 在Web开发中,开发者可能会遇到POST请求被发送了两次的情况,如下图: 尤其是在处理跨域请求时。这种现象可能让开发者感到困惑,但实际上它是浏览器安全机制…

AI数据分析:根据Excel表格数据进行时间序列分析

ChatGPT中输入提示词: 你是一个Python编程专家,要完成一个Python脚本编写的任务,具体步骤如下: 读取Excel表格:"F:\AI自媒体内容\AI行业数据分析\toolify月榜\toolify2023年-2024年月排行榜汇总数据.xlsx"…

SQL 表连接(表关联)

目录 一、INNER JOIN(内连接,等值连接) 二、LEFT JOIN(左连接) 三、RIGHT JOIN(右连接): 一、INNER JOIN(内连接,等值连接) 用途:获取两个表中字段能匹配上…

【stable diffusion】ComfyUI扩展安装以及”127.0.0.1拒绝了我们的连接请求“解决记录

目录 扩展安装”127.0.0.1拒绝了我们的连接请求“解决记录操作1操作2操作3操作4总结扩展安装 虽然大家都推荐将扩展包直接放到extension文件夹的方式,但我还是推荐直接在sd webui的扩展处下载,酱紫比较好维护一点,我个人感觉。 按照上图顺序点击会出现”URLError: <url…

[自动驾驶 SoC]-3 英伟达Orin

NVIDIA Jetson AGX OrinTM series (资料来源&#xff1a;nvidia-jetson-agx-orin-technical-brief.pdf) 1 整体介绍 1) Orin SoC结构 Orin SoC&#xff0c;如下图所示&#xff0c;由一个NVIDIA Ampere architecture GPU, Arm Cortex-A78AE CPU, 下一代深度学习核视觉处理加速…

python相关知识-logging日志、property属性、上下文管理器、生成器等

1.logging日志 目的&#xff1a; 1.可以很方便的了解程序的运行情况 2.可以分析用户的操作行为、喜好等信息 3.方便开发人员检查bug 级别介绍&#xff1a; 1.DEBUG&#xff1a;程序调试bug时使用 2.INFO&#xff1a;程序正常运行时使用 3.WARNNING&#xff1a;程序未按…

学会python——读取大文本文件(python实例六)

目录 1、认识Python 2、环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3、读取大文本文件 3.1 代码构思 3.2 代码示例 3.3 运行结果 4、总结 1、认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强…

基于机器学习的变频器故障诊断方法(MATLAB,Python)

变频器故障数据由MATLAB Simulink生成。 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.neighbors import KNeighborsClassifier from sklearn.svm import SVC from sklearn.ensemble import RandomForestClass…

UniVue更新日志:使用Carousel组件实现轮播图效果

github仓库 稳定版本仓库&#xff1a;https://github.com/Avalon712/UniVue 开发版本仓库&#xff1a;https://github.com/Avalon712/UniVue-Develop UniVue扩展框架-UniVue源生成器仓库&#xff1a;https://github.com/Avalon712/UniVue-SourceGenerator 更新说明 今天的更…

【面试干货】String、StringBuilder、StringBuffer 的区别

【面试干货】String、StringBuilder、StringBuffer 的区别 1、String2、StringBuffer3、StringBuilder4、性能对比5、使用建议 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java中&#xff0c;String、StringBuilder和StringBuffer是用…