数据结构——单链表详解(超详细)(2)

前言:

  上一篇文章小编简单的介绍了单链表的概念和一些函数的实现,不过为了保证文章的简洁,小编把它分成了两篇来写,这一篇小编紧接上一篇文章继续写单链表函数功能的实现:

目录:

1.单链表剩余函数的编写

1.1.单链表查找数据

1.2.在指定位置之前插入数据

1.3.删除指定结点

1.4.在指定位置之后插入数据

1.5.删除指定位置之后的结点

1.6.单链表的销毁

2.完整的代码呈现

正文:

1.单链表剩余函数的编写

1.1.单链表查找数据

//查找

SLTNode* SLTFind(SLTNode* phead, SLdate x);

  对于单链表的查找数据,这就类似顺序表的查找数据,我们要遍历所有的单链表,直到找到我们想要的数据位置,加入没有我们想要的数据,那么我们可以直接返回一个空地址就好了,此时我们不必再传单链表指针的地址了,因为我们本质上并没有对头结点进行改变 ,所以我们仅需把头节点传过去就可以,在函数内部,我们想要遍历顺序表,就要采用循环,来进行一个一个结点的查找,下面是代码呈现:

SLTNode* SLTFind(SLTNode* phead, SLdate x)
{
	SLTNode* pour = phead;
	while (pour)
	{
		if (pour->date == x)
		{
			return pour;
		}
		pour = pour->next;
	}
	return NULL;
}

 1.2.在指定位置之前插入数据

    这里我们可以用到我们上面用到的查找函数,因为它是返回的结点,所以我们可以通过它来确定我们想要的指定位置,因为单链表只可以往后一步一步的走,而不能往前找数据,所以我们得先确认我们想要找的位置,下面我们涉及指定位置的时候,都要引用一下查找指定位置这个函数,下面我们来进行这个函数的讲解,这个函数其实也分为两种情况

  第一种情况是指定位置就是头结点,此时这就类似于我们之前写过的头插函数,我们直接引用头插函数就可以实现这个功能。

  第二种情况就是普通情况,我们要在指定位置之前插入数据,所以我们需要找到指定位置之前的数据,所以这里我们要用用到头结点,并且用到前面讲的创建新结点的函数(因为此文章涉及了不少插入数据的函数,所以我会把创建新结点的代码再写一遍!),我们让一个结点放入头结点数据,然后一步一步往后走,直到走到指定位置之前,之后我们可以讲新节点的下一个结点设置为指定结点,然后再把之前指定位置之前的结点的下一个结点设置为新结点,这里我们便可以完成在指定位置之后插入数据,下面小编先给上图文解释:

新结点的创建:

SLTNode* SLTbuynode(SLdate x)
{
	SLTNode* pour = (SLTNode*)malloc(sizeof(SLTNode));
	assert(pour);
	pour->date = x;
	pour->next = NULL;
	return pour;
}

头插函数: 

void SLTPushFront(SLTNode** pphead, SLdate x)
{
	assert(pphead);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

函数部分:

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x)
{
	assert(pos && pphead && *pphead);
	if (pos == *pphead)
	{
		SLTPushFront(pphead,x);   //这个是头节点就是pos的特殊情况,我们在指定位置之前插入数据的时候,是会先找到指定位置之前的数据以及之后的,头节点的话找不到数据了
	}
	else
	{
		SLTNode* newnode = SLTbuynode(x);
		SLTNode* pour = *pphead;
		SLTNode* pur = NULL;
		while (pour != pos)
		{
			pur = pour;
			pour = pour->next;
		}
		assert(pur);
		newnode->next = pour;
		pur->next = newnode;
	}

1.3.删除指定结点

  删除指定结点的操作,同样也和上面的函数一样,我们也分为两种情况进行讨论:

  第一种情况就是我们想要删除的指定节点就是头结点,此时我们只需要调用一下头删函数就可以实现。

  第二种情况自然就是普通情况,此时我们依旧需要用到头结点,因为我们需要知道指定结点之前的结点,之后我们只需要让前一个结点的下一个结点直接指向指定结点的下一个结点,之后我们在把指定节点给释放掉,就可以实现指定节点删除操作,下面小编先给上图文解释:

头删函数:

void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pour = *pphead;
	*pphead = (*pphead)->next;
	free(pour);
	pour = NULL;
}

删除指定结点函数:

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead && pos <= *pphead);
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* pour = *pphead;
		SLTNode* pur = NULL;
		while (pour != pos)
		{
			pur = pour;
			pour = pour->next;
		}
		assert(pur);
		pur -> next = pur->next -> next;
		free(pour);
		pour = NULL;
	}
}

1.4.在指定位置之后插入数据

  小编在上面已经讲述了指定位置之前如何插入数据了,那么现在可以讲述在指定位置之后插入数据了,这个其实比上面那个简单多了,因为此时我们不需要用到指定位置之前的结点了,所以我们不必在使用头结点了,此时我们依然需要先创建一个新节点,之后我们在讲新结点之后的结点变为我们指定位置之后的结点,指定位置之后的结点·在指向新节点,这里就可以实现在指定位置之后插入新数据了,下面小编先给上图文解释,再给上代码呈现: 

  

void SLTInsertAfter(SLTNode* pos, SLdate x)
{
	assert(pos);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

1.5.删除指定位置之后的结点

  有插入必会有删除,下面我们将要讲述最后一个重要的函数,那么就是删除指定位置之后的结点,这个,同样也是非常的简单,我们只需要把指定结点的下一个结点变为下一个的下一个的结点,再把原来指定位置之后的结点释放掉就好了,下面我们直接给出图像说明:

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* pour = pos -> next;
	pos->next = pos->next->next;
	free(pour);
	pour = NULL;
}

1.6.单链表的销毁

  单链表有创建,自然就会有销毁,单链表的销毁也不算很难,毕竟难的部分都在上面,轻舟已过万重山,对于单链表的销毁其实就是把每个结点给free掉,我们可以设置一个指针作为头结点,在设置一个指针用来给予上一个指针,每销毁完一个结点,我们把第二个指针的内容给予第一个指针,然后让第二个指针继续往后走,这样我们通过循环的知识可以做到每个结点的删除,循环结束完以后,别忘了对头结点进行销毁,这样我们便可以完成对于单链表的销毁了,下面直接给上代码:

void SListDestroy(SLTNode** pphead)
{
	assert(*pphead && pphead);
	SLTNode* pour = *pphead;
	while (pour)
	{
		SLTNode* next = pour->next;
		free(pour);
		pour = next;
	}
	*pphead = NULL;
}

2.完整的代码呈现

  现在,小编已经把单链表大部分内容给讲完了,但单链表实现的功能远不止这些,感兴趣的读者朋友可以再继续探索,小编这里给上大家单链表的完整代码:

SList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLdate;   //方便后面整体类型的改变


//创立一个单链表
typedef struct Slist {
	//先设置一个类型
	SLdate date;
	struct Slist* next;   //存放下一个节点的地址
}SLTNode;



//链表的打印
void SLTprintf(SLTNode* phead);

//开始正式环节了
//尾插
void SLTPushBack(SLTNode** pphead, SLdate x);

//头插
void SLTPushFront(SLTNode** pphead, SLdate x);

//尾删
void SLTPopBack(SLTNode** pphead);

//头删

void SLTPopFront(SLTNode** pphead);

//查找

SLTNode* SLTFind(SLTNode* phead, SLdate x);

//在指定位置之前插入数据

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x);

//删除pos节点

void SLTErase(SLTNode** pphead, SLTNode* pos);


//在指定位置以后插入数据

void SLTInsertAfter(SLTNode* pos, SLdate x);

//删除pos之后的节点

void SLTEraseAfter(SLTNode* pos);


//销毁链表
void SListDestroy(SLTNode** pphead);

SList.c

#include"SList.h"
void SLTprintf(SLTNode* phead)
{
	SLTNode* pour = phead;    //这么做是为了保证头节点不会发生改变
	while (pour)
	{
		printf("%d->", pour->date);
		pour = pour->next;
	}
	printf("NULL\n");
}   //这个操作是打印单链表的数据


SLTNode* SLTbuynode(SLdate x)
{
	SLTNode* pour = (SLTNode*)malloc(sizeof(SLTNode));
	assert(pour);
	pour->date = x;
	pour->next = NULL;
	return pour;
}

void SLTPushBack(SLTNode** pphead, SLdate x)
{
	//首先可以先建一个函数,这个函数是来开辟一个新节点的(后面想要插入直接调用就好了)
	assert(pphead);
	SLTNode * p = SLTbuynode(x);
	if (*pphead == NULL)  //首先判断
	{
		*pphead = p;
	}
	else
	{
		SLTNode* pour = *pphead;
		while (pour -> next)
		{
			pour = pour->next;
		}
		pour->next = p;
	}
}



void SLTPushFront(SLTNode** pphead, SLdate x)
{
	assert(pphead);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}



void SLTPopBack(SLTNode** pphead)
{
	assert(pphead && *pphead);
	if ((*pphead)->next == NULL)
	{
		*pphead = NULL;
	}
	else
	{
		SLTNode* pour = *pphead;
		SLTNode* plist = NULL;
		while (pour->next)
		{
			plist = pour;
			pour = pour->next;
		}
		plist->next = NULL;
		free(pour);
		pour = NULL;
	}
}



void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pour = *pphead;
	*pphead = (*pphead)->next;
	free(pour);
	pour = NULL;
}



SLTNode* SLTFind(SLTNode* phead, SLdate x)
{
	SLTNode* pour = phead;
	while (pour)
	{
		if (pour->date == x)
		{
			return pour;
		}
		pour = pour->next;
	}
	return NULL;
}


void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x)
{
	assert(pos && pphead && *pphead);
	if (pos == *pphead)
	{
		SLTPushFront(pphead,x);   //这个是头节点就是pos的特殊情况,我们在指定位置之前插入数据的时候,是会先找到指定位置之前的数据以及之后的,头节点的话找不到数据了
	}
	else
	{
		SLTNode* newnode = SLTbuynode(x);
		SLTNode* pour = *pphead;
		SLTNode* pur = NULL;
		while (pour != pos)
		{
			pur = pour;
			pour = pour->next;
		}
		assert(pur);
		newnode->next = pour;
		pur->next = newnode;
	}
}



void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead && pos <= *pphead);
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* pour = *pphead;
		SLTNode* pur = NULL;
		while (pour != pos)
		{
			pur = pour;
			pour = pour->next;
		}
		assert(pur);
		pur -> next = pur->next -> next;
		free(pour);
		pour = NULL;
	}
}



void SLTInsertAfter(SLTNode* pos, SLdate x)
{
	assert(pos);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}



void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* pour = pos -> next;
	pos->next = pos->next->next;
	free(pour);
	pour = NULL;
}



void SListDestroy(SLTNode** pphead)
{
	assert(*pphead && pphead);
	SLTNode* pour = *pphead;
	while (pour)
	{
		SLTNode* next = pour->next;
		free(pour);
		pour = next;
	}
	*pphead = NULL;
}

总结:

  可算快马加鞭的肝完这篇文章了,小编本来想上一篇文章就结束单链表的,但是觉得万字文章有点太多了内容,于是分装成两篇来书写,因为小编是学完了单链表就开始写的文章,有一些地方可能有错误或者文笔显得很繁琐,各位读者朋友见谅,如果有错误的话,恳请在评论区指出,小编会及时的更正,那么,我们下一篇文章见啦! 

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

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

相关文章

使用Windows Linux 子系统安装 Tensorflow,并使用GPU环境

在Microsoft Store商店安装Ubuntu 20.04 使用 nvidia-smi 命令查看GPU信息&#xff0c;查看支持的CUDA版本&#xff0c;这里最高支持11.7 安装cuda工具集 进入官网&#xff1a;CUDA Toolkit Archive | NVIDIA Developer&#xff0c;现在对应版本&#xff0c;点击 配置平台&…

【Django+Vue3 线上教育平台项目实战】登录功能模块之短信登录与钉钉三方登录

文章目录 前言一、几个关键概念1.HTTP无状态性2.Session机制3.Token认证4.JWT 二、通过手机号验证码登录1.前端短信登录界面2.发送短信接口与短信登录接口3.Vue 设置interceptors拦截器4. 服务端验证采用自定义中间件方式实现5. 操作流程及效果图如下&#xff1a; 三、通过第三…

对某根域的一次渗透测试

前言 两个月之前的一个渗透测试项目是基于某网站根域进行渗透测试&#xff0c;发现该项目其实挺好搞的&#xff0c;就纯粹的没有任何防御措施与安全意识所以该项目完成的挺快&#xff0c;但是并没有完成的很好&#xff0c;因为有好几处文件上传没有绕过&#xff08;虽然从一个…

【Java】数据类型及类型转换

数据类型 Java语言的数据类型分为两大类&#xff1a; 基础数据类型引用数据类型 基础数据类型 基础数据类型包括以下8种&#xff1a; 类型名称关键字占用内存取值范围区间描述字节型byte1 字节-128~127-27~27-1短整型short2 字节-32768~32767-215~215-1整型int4 字节-2147…

nftables(7)集合(SETS)

简介 在nftables中&#xff0c;集合&#xff08;sets&#xff09;是一个非常有用的特性&#xff0c;它允许你以集合的形式管理IP地址、端口号等网络元素&#xff0c;从而简化规则的配置和管理。 nftables提供了两种类型的集合&#xff1a;匿名集合和命名集合。 匿名集合&…

高职院校人工智能人才培养成果导向系统构建、实施要点与评量方法

一、引言 近年来&#xff0c;人工智能技术在全球范围内迅速发展&#xff0c;对各行各业产生了深远的影响。高职院校作为培养高技能人才的重要基地&#xff0c;肩负着培养人工智能领域专业人才的重任。为了适应社会对人工智能人才的需求&#xff0c;高职院校需要构建一套科学、…

大模型产品琳琅满目,企业应该如何选择?

AI 和大模型方兴未艾&#xff0c;我们每天都在看到和尝试不同版本、不同品牌的大模型产品&#xff0c;它们的能力各不相同。无论是个人还是企业&#xff0c;都在思考如何尽早地参与进来到大模型的浪潮当中来。 目前&#xff0c;一些先锋企业已经将 AI 和大模型融入到他们的日常…

C#学习

C#学习 1.B站丑萌气质狗C#的循环-判断泛型错误处理面向对象static的使用定义showInfo类和Hero类 在这里插入图片描述 然后在该解决方案add新建一个类库&#xff0c;点击rebuild&#xff0c;会在bin文件夹下生成.dll文件 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direc…

无人机之图传距离的决定因素

一、发射功率&#xff1a;图传设备的发射功率越大&#xff0c;信号能够传播的距离就越远 二、工作频段&#xff1a;不同频段具有不同的传播特性&#xff0c;一些频段在相同条件下可能具有更远的传输距离。 三、天线性能&#xff1a;优质的天线可以增强信号的发送和接收能力&a…

php相关

php相关 ​ 借鉴了小迪安全以及各位大佬的博客&#xff0c;如果一切顺利&#xff0c;会不定期更新。 如果感觉不妥&#xff0c;可以私信删除。 默认有php基础。 文章目录 php相关1. php 缺陷函数1. 与2. MD53. intval()4. preg_match() 2. php特性1. php字符串解析特性2. 杂…

jdk22+maven环境配置教程+idea的maven环境配置(Windows系统)

前言 jdk是Java开发必要的编程环境&#xff0c;idea是常用的Java开发工具&#xff0c;这里着重解释一下maven。 maven就是我们经常看见的pom.xml文件&#xff0c;maven有以下三点功能&#xff1a; 1.项目构建&#xff08;可以帮助我们更快速的打包、构建项目&#xff09; 2.依…

<数据集>钢铁缺陷检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1800张 标注数量(xml文件个数)&#xff1a;1800 标注数量(txt文件个数)&#xff1a;1800 标注类别数&#xff1a;6 标注类别名称&#xff1a;[crazing, patches, inclusion, pitted_surface, rolled-in_scale, scr…

Blender使用(二)点线面基本操作

Blender使用之点线面 1.编辑模式 tab键进行切换&#xff0c;为了方便菜单调出&#xff0c;可以设置键位映射为拖动时的饼菜单。 设置好后&#xff0c;按住tab键移动鼠标(注意不要点击鼠标)&#xff0c;即可弹出编辑菜单。 默认是点模式&#xff0c;在左上角可进行点线面的切换…

C++从入门到精通(第2版) 中文电子版

前言 C&#xff08;c plus plus&#xff09;是一种计算机高级程序设计语言&#xff0c;由C语言扩展升级而产生&#xff0c;最早于1979年由本贾尼斯特劳斯特卢普在AT&T贝尔工作室研发。C既可以进行C语言的过程化程序设计&#xff0c;又可以进行以抽象数据类型为特点的基于对…

[C++] 由浅入深理解面向对象思想的组成模块

文章目录 (一) 类的默认成员函数(二) 构造函数构造函数的特征构造函数示例无参构造带参构造 冲突:全缺省参数的构造函数与无参构造函数 &#xff08;三&#xff09;析构函数特性析构函数的析构过程解析 &#xff08;四&#xff09;拷贝构造函数什么是拷贝构造&#xff1f;特性为…

H2数据库启动时,设置非“全零监听”

全零监听 全零监听&#xff08;即将监听地址设置为全零地址&#xff0c;如IPv4中的0.0.0.0或IPv6中的::&#xff09;在网络服务配置中确实存在一定的安全风险。以下是全零监听可能带来的安全风险&#xff1a; 1. 暴露服务到不安全网络 全网段监听&#xff1a;将监听地址设置…

nuitka 打包python程序成windows exe可执行文件

参考&#xff1a; https://www.zhihu.com/question/281858271/answer/2466245521 https://www.zhihu.com/question/281858271 https://zhuanlan.zhihu.com/p/689115995 https://blog.csdn.net/Pan_peter/article/details/136411229 下载&#xff1a; pydantic-2.6.1 pydantic-…

【linux高级IO(三)】初识epoll

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux高级IO 1. 前言2. 初识e…

微服务到底是个什么东东?

微服务架构是一种架构模式&#xff0c;它提倡将单一应用程序划分成一组小的服务&#xff0c;服务之间互相协调、互相配合&#xff0c;为用户提供最终价值。 每个服务运行在其独立的进程中&#xff0c;服务和服务间采用轻量级的通信机制互相沟通&#xff08;通常是基于 HTTP 的…

在设计电气系统时,电气工程师需要考虑哪些关键因素?

在设计电气系统时&#xff0c;电气工程师需要考虑多个关键因素&#xff0c;以确保系统的安全性、可靠性、效率和经济性。我收集归类了一份plc学习包&#xff0c;对于新手而言简直不要太棒&#xff0c;里面包括了新手各个时期的学习方向编程教学、问题视频讲解、毕设800套和语言…