[数据结构] -- 单链表

news2025/4/16 11:00:51

🌈 个人主页:白子寰
🔥 分类专栏: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;
}


 

 

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

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

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

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

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

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

相关文章

订单id的设计问题探讨

如何设计一个订单id 设计一个订单ID系统需要考虑多个因素,包括唯一性、排序性(时间顺序)、可读性(可选)以及系统的扩展性和性能。结合这些因素,可以选择不同的方案来生成订单ID。以下是几种常见的订单ID设…

【Nodejs-多进程之Cluster】

cluster 模块是 Node.js 提供的一个用于多进程的模块,它可以轻松地创建一组共享同一个服务器端口的子进程(worker进程)。通过使用 cluster 模块,可以充分利用多核系统,提高应用程序的性能和可靠性。 基本原理 cluste…

文心智能体,零代码构建情感表达大师智能体

前言 随着智能体技术的突飞猛进,各行各业正迎来前所未有的变革与机遇。智能体,作为人工智能领域的重要分支,以其自主性、智能性和适应性,正逐步渗透到我们生活的每一个角落,成为推动社会进步和科技发展的新动力。 为了…

秒级达百万高并发框架Disruptor

1、起源 Disruptor最初由lmax.com开发,2010年在Qcon公开发表,并于2011年开源,企业应用软件专家Martin Fowler专门撰写长文介绍,同年它还获得了Oracle官方的Duke大奖。其官网定义为:“High Performance Inter-Thread M…

山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(十九)- JUC(5)

synchronized优化原理 轻量级锁 如果一个对象有多个线程访问,但多线程访问的时间是错开的(没有竞争),可以用轻量级锁优化 Slf4j(topic "c.ExerciseTransfer")public class Test {​static final Object obj new Obj…

利用天气API接口自己DIY一个预报小管家

天气预报查询API 是一种实用的日常工具,它通过编程方式为开发者提供实时的天气数据。开发者可以通过简单的代码调用,与天气预报服务提供商进行交互,获取特定地区的天气信息,如温度、湿度、风速、风向、降水量等,以及未…

【智能优化算法】粒子群优化算法(PSO)【附python实现代码】

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 路虽远,行则将至&#…

微信小程序上线必备:SSL证书申请以及安装

一、认识ssl证书 1、ssl证书是什么? SSL证书,全称Secure Socket Layer Certificate,是一种数字证书,它遵循SSL(现在通常指TLS,Transport Layer Security)协议标准,用于在客户端&…

ElasticSearch - 删除已经设置的认证密码(7.x)

文章目录 Pre版本号 7.x操作步骤检查当前Elasticsearch安全配置停止Elasticsearch服务修改Elasticsearch配置文件删除密码重启Elasticsearch服务验证配置 小结 Pre Elasticsearch - Configuring security in Elasticsearch 开启用户名和密码访问 版本号 7.x ES7.x 操作步骤 …

1小时从0开始搭建自己的直播平台(详细步骤)

本文讲述了如何从0开始,利用腾讯云的平台,快速搭建一个直播平台的过程。 文章目录 效果图详细步骤准备工作第一步:添加域名并检验cname配置1.先填加一个推流域名2. 点击完下一步,得到一个cname地址3. 将cname地址,配置…

A股重磅!史上最严减持新规,发布!

此次减持新规被市场视为A股史上最严、最全面的规则,“花式”减持通道被全面“封堵”。 5月24日晚间,证监会正式发布《上市公司股东减持股份管理暂行办法》(以下简称《减持管理办法》)及相关配套规则。 据了解,《减持…

开放式耳机哪个品牌音质好用又实惠耐用?五大公认卷王神器直入!

​在现今耳机市场,开放式耳机凭借其舒适的佩戴体验和独特的不入耳设计,备受消费者追捧。它们不仅让你在享受音乐时,仍能察觉周围的声音,确保与人交流无障碍,而且有利于耳朵的卫生与健康。对于运动爱好者和耳机发烧友而…

热题系列章节1

22. 括号生成 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 示例 1: 输入:n 3 输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”] 示例 2&#xff1a…

Ubuntu(22.04)不能上网解决办法

想必大家可能在别的贴子看到用以下指令的方法,但是在22版本的ubuntu是行不通的,问题在于22版本中网络管理器的名字压根不是network-manager,而是 NetworkManager. sudo service network-manager stop sudo rm /var/lib/NetworkManager/Netw…

力扣hot100学习记录(八)

206. 反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 题意 给一个链表,将链表进行翻转 思路 用两个指针维护相邻两个点,每次把后面一个点指向前一个点,直到后一个点指向空,最后把…

ESP32-C3模组上跑通OTA升级(8)

接前一篇文章:ESP32-C3模组上跑通OTA升级(7) 本文内容参考: 杂项系统 API - ESP32 - — ESP-IDF 编程指南 latest 文档 《ESP32-C3 物联网工程开发实战》 乐鑫科技 特此致谢! 七、固件版本 将不同功能的固件标记为…

阿里云Linux 3.2104 LTS 64位安装SVN服务器

直接按步骤 yum install subversion 写y就行 主要是看看安装了那些文件 rpm -ql subversion 主要是为了创建版本库而准备,这个能一遍创建就一遍创建,不行就逐个创建。能创就忽略下面两个mkdir步骤。 mkdir /home/svn/groupRepos 根据新建目录作为版本…

嵌入式作业5

在函数main.c中初始化三种颜色的灯: gpio_init(LIGHT_BLUE, GPIO_OUTPUT, LIGHT_OFF); //蓝灯 gpio_init(LIGHT_GREEN, GPIO_OUTPUT, LIGHT_OFF); //绿灯 gpio_init(LIGHT_RED, GPIO_OUTPUT, LIGHT_OFF); //红灯 同时为了响应以上修改,进入isr。c…

1098: 堆的判断

解法&#xff1a; 堆是完全二叉树 用数组来存储 然后用定义判定 #include<iostream> #include<vector> using namespace std; int main() {int n;cin >> n;vector<int> vec(n);for (int i 0; i < n; i) cin >> vec[i];for (int i 0; i &…

【第1章】SpringBoot入门

文章目录 前言一、版本要求1. SpringBoot版本2. 其他2.1 System Requirements2.2 Servlet Containers2.3 GraalVM Native Images 3. 版本定型 二、新建工程1.IDEA创建 ( 推荐 ) \color{#00FF00}{(推荐)} (推荐)2. 官方创建 三、第一个SpringBoot程序1. 引入web2. 启动类3. 启动…