[数据结构] -- 单链表

🌈 个人主页:白子寰
🔥 分类专栏:C++打怪之路,python从入门到精通,数据结构,C语言,C语言题集👈 希望得到您的订阅和支持~
💡 坚持创作博文(平均质量分82+),分享更多关于深度学习、C/C++,python领域的优质内容!(希望得到您的关注~)

 

目录

链表

概念

结构

链表的分类

单向链表和双向链表

 带头(哨兵位) 或 不带头(哨兵位) 链表

顺序表和链表的优缺点

在SList.h文件中

定义单链表的结构

实现单链表的接口/方法

在SList.c文件中 

打印单链表(遍历链表)

开辟空间

开辟空间molloc返回值问题

尾部插入元素

传参的时候为什么要传二级指针? 

测试尾插 

头部插入元素 

测试 

尾部删除元素 

测试 

头部删除元素 

测试 

查找元素 

 在指定位置之前插入数据

测试 

删除pos节点 

测试 

在指定位置之后插入数据

删除pos之后的节点 

测试 

销毁链表 




链表

概念

链表是一种物理存储结构上非连续、非顺序的存储结构,

数据元素的逻辑顺序是通过链表中的指针链接次序实现的,


单链表一般不会单独使用,
只有头插和头删实现简单且效率高

结构

链表也属于线性表,线性表在物理上存储时,
通常以数组(顺序表)和链式结构(链表)的形式存储,
链式结构在逻辑上是连续的,但在物理上不一定连续
                 
现实中的结点一般是从堆上申请出来的
              
从堆上申请的空间,是按照一定的策略来分配的,
两次申请的空间可能连续,也可能不连续


链表的分类

单向链表和双向链表

单向链表(常用)

双向链表 

 


 带头(哨兵位) 或 不带头(哨兵位) 链表

 带头(哨兵位) 链表

不带头(哨兵位) 链表


循环链表

 

带头双向循环链表(常用)

 



顺序表和链表的优缺点

顺序表链表
存储空间物理上一定连续逻辑上连续,物理上不一定连续
访问随机访问不支持随机访问
任意位置插入或删除元素效率低修改指针指向
应用空间不够扩容,元素高效存储,随机访问分节点,开节点,可以在任意位置插入或删除

 



在SList.h文件中

定义单链表的结构

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;		//数据
	struct SListNode* next; //指针域next
}SLTNode;

 

实现单链表的接口/方法

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

//头部插入删除/尾部插入删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

//查找
SLTNode* SLTFind(SLTNode** phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);


 

在SList.c文件中 

打印单链表(遍历链表)

//SLTNode*,表示 phead 是一个指向 SLTNode 类型的指针。
void SLTPrint(SLTNode* phead)		//接收一个指向链表头节点的指针 phead 作为参数
{
	//一级指针
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL");

	printf("\n");
}

 

开辟空间

//开辟空间
SLTNode* SLTBuyNode(SLTDataType x)
{
	//为一个 SLTNode 类型的结构体分配内存,以便访问和操作这个新分配的 SLTNode 结构体
	//所以返回值为SLTNnode*
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

开辟空间molloc返回值问题

函数原型:

 

 

 


尾部插入元素

//尾部插入元素
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);
	//头结点为空
	if (*pphead == NULL)
	{
		*pphead = newnode;
		return;
	}

	//头结点不为空
	SLTNode* ptail = *pphead;
	while (ptail->next)
	{
		ptail = ptail->next;
	}

	ptail->next = newnode;
}

传参的时候为什么要传二级指针? 

二级指针,在函数内部修改头指针本身的值

一级指针,用于遍历和访问链表

以下的 打印链表和销毁链表函数 说明一级和二级指针的意思 

测试尾插 


 

头部插入元素 

//头部插入元素 
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);

	newnode->next = *pphead;	// *pphead表示头结点
	*pphead = newnode;
}

测试 


 

尾部删除元素 

free作用:

①free(ptail);之后,ptail指针本身的值并未改变,但它所指向的内存已经被释放,因此不应该再使  用这个指针访问已经被释放的内存。

②我们只需要确保不要再使用这个指针来访问内存,而不必将其置为NULL

//尾部删除元素 
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);	//保证头结点不为空

	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}

	//有多个结点
	SLTNode* ptail = *pphead;
	SLTNode* prev = NULL;

	while (ptail->next)
	{
		prev = ptail;
		ptail = ptail->next;
	}
	
	//单链表最后一个结点,只有数据域data且指针域的值是NULL
	// 释放尾节点的内存
	free(ptail);

	// 将尾节点的前驱节点的next置为NULL,实现删除尾节点 
	prev->next = NULL;
}

测试 

头部删除元素 

void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);	//保证头结点不为空

	SLTNode* next = (*pphead)->next;

	free(*pphead);
	*pphead = next;
}

测试 

查找元素 

//查找
SLTNode* SLTFind(SLTNode** phead, SLTDataType x)
{
	assert(phead);

	SLTNode* pcur = *phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}

	return NULL;
}

 在指定位置之前插入数据

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);

	SLTNode* newnode = SLTBuyNode(x);
	//pos刚好是头节点
	if (newnode->next == *pphead)
	{
		SLTPushBack(pphead, x);
		return;
	}

	//pos刚好不是头节点
	SLTNode* pcur = *pphead;
	while (pcur->next != pos)
	{
		pcur = pcur->next;
	}

	pcur->next = newnode;
	newnode->next = pos;
}

测试 

删除pos节点 

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);

	//刚好是头节点
	if (pos == *pphead)
	{
		SLTPopFront(pphead);
		return;
	}

	//不是头节点
	SLTNode* pcur = *pphead;
	while (pcur->next != pos)
	{
		pcur = pcur->next;
	}
	pcur->next = pos->next;
	free(pos);
	pos = NULL;
}

测试 

在指定位置之后插入数据

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = SLTBuyNode(x);

	SLTNode* pcur = pos->next;
	pos->next = newnode;
	newnode->next = pcur;
}

 

删除pos之后的节点 

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);

	SLTNode* p1 = pos->next;
	SLTNode* pcur = pos->next->next;

	pos->next = pcur;

	free(p1);
	p1 = NULL;
}

测试 

 

销毁链表 

//销毁链表
//SLTNode**,表示 pphead 是一个指向 SLTNode* 类型的指针的指针
void SListDesTroy(SLTNode** pphead)
{
	/*  pphead:(二级指针)*/
	assert(pphead);

	//一级指针
	assert(*pphead);//头结点不为空,链表不为空

	SLTNode* pcur = *pphead;	//*pphead 是头指针本身(一级指针),用于遍历和访问链表
	
	//先释放,后置空
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}


 

 

 ***********************************************************分割线*****************************************************************************
完结!!!
感谢浏览和阅读。

等等等等一下,分享最近喜欢的一句话:

“迷失的时候,选择更艰辛的那条路”。

我是白子寰,如果你喜欢我的作品,不妨你留个点赞+关注让我知道你曾来过。
你的点赞和关注是我持续写作的动力!!! 
好了划走吧。

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

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

相关文章

小红书云原生 Kafka 技术剖析:分层存储与弹性伸缩

面对 Kafka 规模快速增长带来的成本、效率和稳定性挑战时,小红书大数据存储团队采取云原生架构实践:通过引入冷热数据分层存储、容器化技术以及自研的负载均衡服务「Balance Control」,成功实现了集群存储成本的显著降低、分钟级的集群弹性迁…

ABB机器人---基础编程

目录 第一章 代码解释 1.1 基础代码 1.1.2 关于 VAR robtarget pos 1.1.3 关于四元数 1.2 机器人初始化程序 1.3 配置通信 (ProfiNet 示例,ABB RAPID) 1.4 设置干涉区 (ABB RAPID) 1.5 示教轨迹和自动过程 (ABB RAPID) 1.6 配置抓手并进行抓取操作 (ABB RA…

[WUSTCTF2020]funnyre

ida打开 mian 函数 不能反汇编,往下翻有一处报红,一看是花指令,还怪长,报红的都nop后,全选按P重新生成函数 三百多个变量,也是不太可能一个个去解了,刚好前两天简单练了一下 angr (…

拓数派入选中电联大数据与统计分会两大重点专项工作组

自中国电力企业联合会大数据与统计分会成立以来,深入贯彻党中央、国务院关于不断做强做优做大我国数字经济有关要求,充分发挥数据要素乘数效应,凝聚行业专家及能源电力产业链各主体力量,持续推进能源电力数据资源交易共享&#xf…

Xinstall:开启携带参数注册新时代,提升用户体验与运营效率

在移动互联网时代,App推广和运营面临着诸多挑战。其中,如何精准追踪用户来源、评估推广效果以及优化用户体验,一直是开发者们关注的焦点。而Xinstall作为一家一站式App全渠道统计服务商,通过其独特的携带参数注册功能,…

electron调试自动更新,不触发下载进度解决方案

调试时候删除掉后缀是.blockmap的文件。如果你的代码在改动不大的情况下发布一个新版本。那个安装器可能会根据这个数据自动合成一个包,而不走网络路径。从而不触发下载进度。

kubectl--操作指令大全

目录 一 kubectl 1 查看版本信息 2 查看资源对象简写 3 查看集群信息 4 配置kubectl自动补全 5 node节点查看日志 二 基本信息查看 1 查看 master 节点状态 2 查看命令空间 3 查看命名空间为default的所有资源 4 创建命名空间app 5 删除命名空间app 6 指定pod控…

外卖系统源码开发全攻略:外卖小程序与后台管理系统的设计与实现

今天,小编将详细介绍外卖系统源码的开发全攻略,从需求分析到设计与实现,为开发者提供全面指导。 一、需求分析 1.用户需求 用户是外卖系统的核心,需满足以下基本需求: -浏览菜单并下单 -实时追踪订单 -多种支付方…

对安卓手机上损坏的 SD 卡进行故障排除:恢复提示和修复

概括 如果您总是在旅途中,那么您很可能每天都在使用 SD 卡。这些微小但功能强大的闪存已经变得和手机的内部存储一样有用。它们可以存储数据并移动您想要的任何数据类型,因为它们在 Android 设备上添加了额外的存储空间。不幸的是,他们可能会…

leetCode-hot100-数组专题之双指针

数组双指针专题 1.同向双指针1.1例题26.删除有序数组中的重复项27.移除元素80.删除有序数组中的重复项 Ⅱ 2.相向双指针2.1例题11.盛最多水的容器42.接雨水581.最短无序连续子数组 双指针在算法题中很常见,下面总结双指针在数组中的一些应用,主要分为两类…

探索自动化办公的新境界:批量操作与智能管理

新书上架~👇全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一、自动化办公的必要性与价值 二、基础操作与自动化脚本 三、Python在自动化办公中的应用…

Java 类加载和实例化对象的过程

1. 类加载实例化过程 当我们编写完一个*.java类后。编译器(如javac)会将其转化为字节码。转化的字节码存储在.class后缀的文件中(.class 二进制文件)。接下来在类的加载过程中虚拟机JVM利用ClassLoader读取该.class文件将其中的字…

DEV--C++小游戏(吃星星(1.2))

目录 吃星星(1.2) 该版本简介更新说明 分部代码 头文件命名空间变量 结构体 角色结构体 星星结构体 打印地图结构体 函数 函数声明 单人模式游戏函数 双人模式游戏函数 开始游戏函数 清屏函数 定点输出函数 隐藏光标函数 输入函数 单人…

【C++】<知识点> 标准和文件的输入输出

目录 一、输入输出操作 1. 相关的类 2. 标准流对象 3. istream类的成员函数 二、流操纵算子 1. 整数流的基数 2. 浮点数精度的流操纵算子 3. 域宽的流操纵算子 4. 其他的流操纵算子 5. 用户自定义流操纵算子 三、文件读写 1. 文本文件的读写 2. 二进制文件的读写 3. 文件读写…

编一个自己的万年历

编一个自己的万年历 前阶段突然想查一下某一天是星期几,于是自己编了一个[小程序][https://blog.csdn.net/weixin_41905135/article/details/138972055?spm1001.2014.3001.5501],但是功能很单一,就是单纯的查是星期几。(虽然用网…

Deep Residual Learning for Image Recognition--论文笔记

论文笔记 论文来源: Deep Residual Learning for Image Recognition 代码来源 还没上传 1论文摘要的翻译 深度神经网络更难训练。我们提出了一个残差学习框架,以简化比以前使用的网络深度大得多的网络的训练。我们明确地将层重新表述为参考层输入的…

十五、Python模块 1、(入门一定看!!!)「长期更新Python简单入门到适用」

首先什么是模块? 小伙伴们经常看我写的教程不难发现,前面我们用过几次模块就是sys的那个,其实python不仅标准库中包含了大量的模块(也被称之为准模块),还有大量的第三方模块,开发者也可以自己发…

Python学习---基于HTTP的服务端基础框架搭建案例

整体功能: 1 创建框架构建相关的文件夹 2 创建app,模块文件 3 在 app模块文件中创建application函数(用于处理请求) 4 将request_handler()中的处理逻辑交由app模块的application函数完成 5 app模块的 application函数返回响应报文 6 在application 文件夹中创建一个…

零基础HTML教程(34)--HTML综合实例

文章目录 1. 背景2. 开发流程2.1 网站功能设计2.2 建立网站目录结构2.3 开发首页2.2 生平简介页2.3 经典诗词页2.4 苏轼图集页2.5 留言板 3. 小结 1. 背景 通过前面33篇文章的学习,我们对HTML有了一个比较全面的了解。 本篇,我们编写一个网站实例&…

C++ RBTree封装mapset

目录 RBTreeNode的声明 RBTree结构 map结构 set结构 改造红黑树 迭代器类 迭代器成员函数 默认成员函数 Insert set map RBTreeNode的声明 template<class T> struct RBTreeNode {RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>*…