一些关于单链表的操作

news2024/11/25 2:34:16

思维导图:

一, 链表

1.1节点的结构

链表是啥样的啊?顾名思义链表就是一种用链子链接起来的表。那这种表是怎么样的啊?

这样的呗:

现在,我们知道了链表的形状了。那我们该如何用编程语言来形成这一种形状呢?

首先,一个链表节点是这样的:

毫无疑问,链表也需要存储一些数据(不然要链表来干嘛?)。而且链表还有一条尾巴(当然这是为了形象而画出来的)这条尾巴又要如何形成呢?现在让我们来想一想尾巴的作用是什么啊?当然是为了找到下一个节点啊,那我们要找到下一个节点的话我们应该用什么去找呢?当然是下一个节点的地址啊!那放地址我们是不是应该要用指针啊?那好,现在我们已经清楚的知道了这一个节点的结构组成了。画图如下:

现在,我们再往下推。首先,我们这一个结构里面含有两种不同类型的数据。不同类型的数据应该用什么数据结构来存储呢?明显是结构体。那我的指针指向的下一个节点是不是又是一个结构体或者空指针呢?那这个指针是不是又可以定义为结构体指针呢?答案是肯定的!

经过以上分析,现在我们就可以把代码写出来了:

代码:

typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;//这里的typedef是为了在后面对这个结构体更好的使用

 1.2节点的生成

现在我们已经把节点的结构搞明白了,我们就要进入第二阶段了。我们要把上面的结构的节点生成一下。生成节点用什么啊?malloc啊!

生成节点代码:

SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));//动态开辟空间
	if (newnode == NULL) {
		printf("malloc fail\n");
		return NULL;
	}
	newnode->data = x;//在数据区内放入数据
	newnode->next = NULL;//先置空指针区
	return newnode;//将新节点返回
}

1.3打印链表

打印链表该怎么做呢?

其实很简单,这样做就行了!

代码:

void SListPrint(SListNode* plist) {
	SListNode* cur = plist;
	while (cur) {
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("->NULL\n");//当链表为空的时候就打印一个NULL来提示一下
}

1.4在链表中插入数据

         现在我们的链表的设计已经写好了,但是这个链表是一个空链表。现在我们就要来给这个链表插入数据。当然,插入数据的方法有很多种。像头插,尾插,中间插入等等……。现在我们就来一 一实现一下。

1.4.1:头插

头插的方法就是生成一个新的节点,然后让新的节点成为新的头节点连接*pplist。然后再移动*pplist的位置让它指向新的节点。

void SListPushFront(SListNode** pplist, SLTDateType x) {
	SListNode* newhead = BuySListNode(x);
		newhead->next = *pplist;
		*pplist = newhead;	
}

其实,这里的头插逻辑基本上没有什么难度。但是这里使用一级指针还是二级指针才是需要花时间去理解的!可以看到我们这里使用的是二级指针,为什么呢?一句话——“你要改变谁就要用谁的指针”。因为在test文件里面我用的是结构体的指针:

SListNode* plist = NULL;

所以我要改变外面的plist的话就要用plist的指针了,因为指针的指针就是二级指针!所以我这里就用了二级指针!!!(解答完毕)

1.4.2:中间插入

先来讲一下我的中间插入的逻辑:我的插入是在我要找到的数据的后一个位置插入,所以找到位置很重要所以我会写一个查找数据位置的函数

代码;

SListNode* SListFind(SListNode* plist, SLTDateType x) {
	assert(plist);
	SListNode* pos = plist;
	while (pos) {
		if (pos->data == x) {
			return pos;//找到了就返回,结束循环
		}
		pos = pos->next;
	}
	printf("你要找的值不存在\n");
	return NULL;//找不到就返回空
}

写完了寻找节点的函数以后,我们就可以来写一个中间插入的函数来。代码如下:

void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);//要对pos断言一下,因为这是中间插入,链表中有节点才能中间插入!
	SListNode* newnode = BuySListNode(x);
	SListNode* next = pos->next;//记录一下pos指向的下一个节点,因为pos->next要被改变!
	pos->next = newnode;
	newnode->next = next;
	
}

1.4.3尾插

尾插?啥是尾插啊?顾名思义,尾插便是找到链表的尾巴然后再来插入,具体的逻辑可以参见代码。

void SListPushBack(SListNode** pplist, SLTDateType x)
{
	SListNode* newtail = BuySListNode(x);
	SListNode* tail = *pplist;
	if (tail == NULL) {//如果链表为空那就直接让*pplist指向newtail
		*pplist = newtail;
	}
	else {
		while (tail->next) {//这里找的是尾节点,所以当tail->next为空时就找到了尾节点了
			tail = tail->next;
		}
		tail->next = newtail;
	}
}

 1.5在链表中删除节点

和链表的插入一样,链表节点的删除也有三种。分别是头删,尾删,中间删除!

1.5.1:头删

头删,顾名思义便是删除头节点。思路很简单,就是调整一下plist的指向让plist指向下一个节点然后释放上一个节点就可以了。

代码:

void SListPopFront(SListNode** pplist) {
	assert(pplist);//pplist不可能为空,所以这里要预防一下
	assert(*pplist);//空链表不能删
	SListNode* del = *pplist;
	*pplist = (*pplist) ->next;
	free(del);
	del = NULL;
}

1.5.2:中间删除

这个操作的实现也是需要和 SListFind搭配使用的,前面我已经将 SListFind写好了。这里我就不再写了,我在这里直接写中间删除的函数就好了。当然这个中间删除函数与前面的中间插入一样,都只是删除中间的节点而不会删除头节点与尾节点。所以依照这个思路,我们便可以将代码写出来:

 代码:

void SListEraseAfter(SListNode* pos) {
	assert(pos);//中间删除不会删除头节点,更不会删除不存在的值。
	assert(pos->next);//也不能删除空指针
	SListNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

1.5.3尾删

顾名思义,尾删就是把链表的尾节点删除掉。删除的逻辑就是找到链表的尾节点的前面一个节点然后将这一个节点中的指向改为空。并把尾节点给释放掉。

代码:

void SListPopBack(SListNode** pplist) {
	assert(pplist);//指针的地址不可能为NULL
	assert(*pplist);//空链表不能尾删
	if ((*pplist)->next == NULL) {
		free(*pplist);
		*pplist = NULL;
	}
	else {
		SListNode* tail = *pplist;
		while (tail->next->next) {
			tail = tail->next;
		}
		SListNode* del = tail->next;
		tail->next = NULL;
		free(del);
		del = NULL;
	}
}

1.6销毁链表

因为我们的链表是malloc出来的一块一块的空间,所以当我们的链表用完以后就要对这些一块一块的空间进行释放。说所以我们又要写一个函数来销毁链表。

代码:

void SListDestroy(SListNode** pplist) {
	assert(pplist);
	assert(*pplist);//链表未创建不能销毁
	SListNode* cur = NULL;
	while (*pplist) {
		cur = (*pplist)->next;
		free(*pplist);
		*pplist = cur;
	}
	*pplist = NULL;
	printf("链表销毁成功!\n");
}

总结:

到这里我们就已经把我能讲的一些关于链表的知识,我所知道的关于链表的知识给讲完了。但是我还是想要总结一下:

1.在我们要操作链表时,比如想要增删时就要用二级指针。

2.当一个变量不可能为空指针时要对它进行assert断言。

3.当一个变量为空时会导致错误时也要对这个变量进行assert断言。

好了,总结完毕!

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

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

相关文章

mysql 安装全过程(linux上二进制包安装)

介绍 mysql 是一种RDBMS 关系型数据库管理系统 Relational Database Management System 关系型数据库将数据保存在不同的表中,而不是放在一个大仓库内,增加了速度,提高了灵活性。 . mysql版本 5.7.x 和 8.0.x 是目前主流。2个…

RT-Thread 在线软件包改为本地软件包的方法

问题描述 RT-Thread 的软件包,使用时,需要手动通过 ENV 工具 更新到 本地的 packages 目录,并且 packages 目录默认不参与 Git 工程管理,软件包多了,并且偶尔需要更改软件包本身的一些代码,这就造成了软件项…

Spring 依赖注入源码

文章目录 依赖注入原始依赖注入方式注解方式寻找注入点注入点进行注入 从BeanFactory中找注入对象总结 依赖注入 具体代码是在AbstractAutowireCapableBeanFactory类的populateBean()方法,此方法中主要做的事情如下: 实例化之后,调用Instan…

【Java校招面试】基础知识(二)——Spring Framework AOP

目录 前言一、Spring Framewwork基础知识二、Spring AOP基础概念1. 切面(Aspect)2. 织入(Weaving)3. 增强(Advice)4. 动态代理 三、JDK动态代理1. 基本用法2. 原理分析 四、CGLib动态代理1. 基本用法2. 原理…

【五一创作】使用Resnet残差网络对图像进行分类(猫十二分类,模型定义、训练、保存、预测)(二)

使用Resnet残差网络对图像进行分类 (猫十二分类,模型定义、训练、保存、预测)(二) 目录 (6)、数据集划分 (7)、训练集增强 (8)、装载数据集 &#xff08…

山东专升本计算机第十一章-新一代信息技术

新一代信息技术 物联网 概念 物联网就是物物相连的互联网,其核心和基础仍然是互联网 计算机,互联网之后信息产业发展的第三次浪潮 推入人类进入智能时代,又称物联时代 三大特征 全面感知 可靠传递 智能处理 • 物联网的最核心 技术架…

阿里云g8i服务器ECS通用型服务器CPU处理器性能测评

阿里云服务器ECS通用型实例规格族g8i采用2.7 GHz主频的Intel Xeon(Sapphire Rapids) Platinum 8475B处理器,3.2 GHz睿频,g8i实例采用阿里云全新CIPU架构,可提供稳定的算力输出、更强劲的I/O引擎以及芯片级的安全加固。阿里云百科分享阿里云服…

JavaScript 入门(1)

script 标签 <scrtipt> 标签可以插入到HTML中的任何位置在很老的代码中需使用type属性&#xff0c;但是现在的代码中不需要 <script type"text/javascript"><!-- ... //--></script>外部脚本 通过src 属性将脚本添加到HTML中 <script …

Maven的全面讲解及如何安装使用

Maven是一种流行的Java项目管理工具&#xff0c;可用于构建、测试、打包和部署Java应用程序。本文将介绍Maven的概念、安装配置、使用方法、生命周期以及IDEA集成Maven的方法。 Maven的概念 Maven是一种基于项目对象模型&#xff08;POM&#xff09;的构建工具。POM是一个XML…

【C++】位运算类题目总结

文章目录 一. 位运算符脑图二. 相关题目1. 统计二进制数中0的个数2. 数组中只出现一次的数字3. 数组中只出现一次的数字 II4. 不用加减乘除做加法 一. 位运算符脑图 二. 相关题目 1. 统计二进制数中0的个数 解题思路&#xff1a;x & (x-1)&#xff1b;它的作用是每次循环…

系统集成项目管理工程师 笔记(第18章:项目风险管理)

文章目录 18.1.2 风险的分类 54318.1.3 风险的性质 544项目风险管理6个过程&#xff08;风险管理、识别风险、实施定性风险分析、实施定量风险分析、规划风险应对、控制风险&#xff09;组织和干系人的风险态度影响因素18.3.3 规划风险管理的输出 550风险识别的原则18.4.2 识别…

针对Vue前后端分离项目的渗透思路

引言 在目前的开发环境下&#xff0c;越来越多的厂商选择 Vue.js 来实现前端功能的编写&#xff0c;且成熟的前端框架已经可以实现后端代码实现的功能&#xff0c;导致后端目前只负责提供 Api 接口和文档&#xff0c;方便前端的同时去调用。本文主要介绍如何针对这类前后端分离…

如何利用几何坐标变换后纠正技术实现倾斜摄影三维模型数据拼接?

如何利用几何坐标变换后纠正技术实现倾斜摄影三维模型数据拼接&#xff1f; 倾斜摄影三维模型数据拼接是指将多个倾斜摄影数据集合并为一个完整的三维模型。在这个过程中&#xff0c;由于不同数据集之间的相对位置和姿态不同&#xff0c;需要进行几何坐标变换以实现数据拼接。…

借用AI工具为视频添加中文字幕,消除语言障碍,母语环境最快速地学习

由于chatgpt的启动&#xff0c;感觉语言已经完全不会成为学习的障碍&#xff0c;突发奇想&#xff0c;在我们查看youtube视频的时候&#xff0c;有没有方便的工具能够将其字幕翻译为中文。这样能够极大提高在youtube学习的效率&#xff0c;于是顺手问了一下ChatGPT&#xff0c;…

Nginx—在linux的ubuntu系统上的安装使用

前言: 有关Nginx的基础知识和使用都在这里Nginx简介和快速入门_北岭山脚鼠鼠的博客-CSDN博客 常用命令: cd /usr/local/nginx/sbin/ ./nginx 启动 ./nginx -s stop 停止 ./nginx -s quit 安全退出 ./nginx -s reload 重新加载配置文件(常用) //在修改配置文件之后使用 p…

教你部署chatgpt商业版源码,支持卡密开通国内使用

教你部署chatgpt商业版源码&#xff0c;支持卡密开通国内使用 当今&#xff0c;人工智能技术在各个领域的应用越来越广泛&#xff0c;其中自然语言处理是非常重要的一环。OpenAI 的 GPT 模型是自然语言处理领域的一项重要技术&#xff0c;它可以根据已有的文本数据&#xff0c;…

Java 怎样实现代理模式,有什么优缺点

一、介绍 代理模式是一种常见的设计模式&#xff0c;它可以为其他对象提供一种代理以控制对这个对象的访问。代理对象具有与被代理对象相同的接口&#xff0c;客户端无需知道代理对象和被代理对象的区别。代理模式可以应用于各种不同的场景&#xff0c;例如远程代理、虚拟代理…

Ubantu docker学习笔记(九)容器监控 自带的监控+sysdig+scope+cAdvisor+prometheus

文章目录 一、Docker命令监控二、Sysdig2.1介绍2.2 基本操作2.2.1 切换视图2.2.2 查看标签含义2.2.3 排序2.2.4 查看内部进程2.2.5 查找2.2.6 暂停2.2.7 上一级2.2.8 退出 三、Weave Scope3.1介绍3.2基本操作3.2.1 显示容器3.2.2 选择容器3.2.3 按照CPU使用情况排序3.2.4 控制容…

手动开发 简单的 Spring 基于 XML 配置的程序

目录 手动开发- 简单的 Spring 基于 XML 配置的程序 需求说明 思路分析 WyxApplicationContextTest xml配置 注意 手动开发- 简单的 Spring 基于 XML 配置的程序 需求说明 1. 自己写一个简单的 Spring 容器, 通过读取 beans.xml&#xff0c;获取第 1 个 JavaBean: Mon…

【建议收藏】Pandas(一)——初见Series

文章目录 &#x1f4da;引言&#x1f4d6;库的安装以及一些说明&#x1f4d1;库的安装&#x1f4d1;一些说明 &#x1f4d6;Series&#x1f4d1;创建一个Series&#x1f516;从列表创建Series&#x1f516;从字典创建Series&#x1f516;标量创建Series &#x1f4d1;Series的特…