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

news2024/11/24 12:31:10

请添加图片描述

🔥引言

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

请添加图片描述
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.coloradmin.cn/o/1830411.html

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

相关文章

3. ceph-mimic版本部署

ceph-mimic版本部署 一、ceph-mimic版本部署1、环境规划2、系统基础环境准备2.1 关闭防火墙、SELinux2.2 确保所有主机时间同步2.3 所有主机ssh免密2.4 添加所有主机解析 3、配置ceph软件仓库4、安装ceph-deploy工具5、ceph集群初始化6、所有ceph集群节点安装相关软件7、客户端…

【C++】多态|原理|override|final|抽象类|多继承虚函数表|对象模型|虚表打印|(万字详解版)

目录 ​编辑 一.多态的概念 二.多态的构建 虚函数 重写 虚函数重写的例外 协变 隐藏 析构函数的重写 三.重载、重写(覆盖)、隐藏(重定义)的对比 四.C11新增的 override 和 final override final 五.抽象类 六.多态的原理 虚函数表 总结: 引用…

CMake多行注释以及通过Message打印不同级别日志

1 CMake注释 1.1 单行注释 CMake中单行注释时以 # 开头。 # 指定CMake最低版本cmake_minimum_required(VERSION 3.20)# 这是注释project(myproject)1.2 多行注释 多行注释时,以 #[[ 开头,以 ]] 结尾,中间都可以写注释内容。3.0之前的版本…

MySQL面试重点-1

1. 数据库基础知识: DDL、DML、DQL、DCL的概念与区别? DDL(数据定义语言):创建(CREATE)数据库中的各种对象:表、视图、索引等DML(数据操纵语言)&#xff1a…

点积和叉积

文章目录 1、向量的点积2、向量的叉积3、矩阵的点积4、矩阵的叉积 1、向量的点积 数量积又称标量积(Scalar product)、点积(Dot product),在欧几里得空间(Euclidean space)中称为内积&#xff…

为何云原生是未来?企业IT架构的颠覆与重构(上)

🐇明明跟你说过:个人主页 🏅个人专栏:《未来已来:云原生之旅》🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、什么是云原生 2、云原生的背景和起源 背景 起源 关…

酷开会员丨酷开系统K歌模式,父亲节的家庭欢聚时光

K歌以其独特的魅力,为家庭娱乐带来了无限乐趣。想象一下,父亲节这天,打开电视进入K歌频道,与家人一起嗨唱,客厅里充满了欢声笑语,酷开系统的K歌应用也就成为了连接亲情的桥梁,让爸爸们都能在这个…

CSS-0_1 CSS和层叠(样式优先级、内联样式、选择器 用户代理样式)

CSS 的本质就是声明规则 ——《深入解析CSS》 文章目录 CSS层叠和优先级用户代理样式请和用户代理样式和谐相处 选择器单选择器的优先级选择器组的优先级关于选择器的其他源码顺序尽可能的选择优先级低的选择器 内联样式内联样式和JavaScript !important多个 !important 碎碎念…

Wing FTP Server v7.2.0 解锁版安装教程 (跨平台的专业FTP服务器软件)

前言 Wing FTP Server是一款跨平台的专业FTP服务器软件, 支持可扩展处理器架构采用异步IO处理, 在速度和效率方面领先于其他同类产品. 它在高负载的情况下也能持续地正常运行, 非常适合企业文件传输. 通过基于Web管理端, 何时何地都能轻松管理远程的服务器. 除了基本功能外, 它…

qt笔记之qml和C++的交互系列(二):rootObject

qt笔记之qml和C的交互系列(二):rootObject code review! —— 2024-06-17 杭州 夜 文章目录 qt笔记之qml和C的交互系列(二):rootObject一.使用rootObject例程1.运行2.main.cpp3.main.qml3.用于Debug的加长版main.cpp 二.QML文件的根对象详解基本概念常…

蜂鸣器:基础(1)

蜂鸣器:基础(1) 原文: In this tutorial, we are going to learn how to use the buzzer with Arduino, In detail, we will learn: 在本教程中,我们将学习如何将蜂鸣器与Arduino一起使用,将学习&#xff…

C的I/O操作

目录 引言 一、文件与目录操作 1. 打开与关闭文件 2. 文件读写操作 3. 文件定位与错误处理 二、字符流与字节流 1. 字符流处理 2. 字节流处理 三、序列化与反序列化 1. 序列化 2. 反序列化 四、新的I/O(NIO) 表格总结 文件与目录操作 字符…

证明 均匀分布 的期望和方差

均匀分布 均匀分布(Uniform Distribution)是一种常见的连续型概率分布,其中随机变量在给定区间内的每个值都有相同的概率。假设随机变量 ( X ) 在区间 ([a, b]) 上服从均匀分布,记作 均匀分布的概率密度函数(PDF&am…

好用的库函数,qsort函数大详解(干货满满!)(进阶)

前言: 小编在上一篇文章说了这一篇将要写qsort函数的模拟实现,那么废话不多说,现在开始进入今天的代码之旅喽! 目录: 1.qsort函数的模拟实现的逻辑和思路 2.qsort函数模拟实现的代码实现 3.代码展示 1.qsort函数的模…

YOLOv9独家提点|加入MobileViT 、SK 、Double Attention Networks、CoTAttention等几十种注意力机制(五)

本文介绍了YOLOv9模型的改进,包括加入多种注意力机制,如SE、CBAM、ECA和SimAM。此外,还探讨了MobileViT轻量级视觉Transformer在移动设备上的应用,以及SelectiveKernelNetworks和A2-Nets的双注意力结构。最后,CoTAttention网络在视觉问答任务中的改进展示了跨模态注意力交…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 6月17日,星期一

每天一分钟,知晓天下事! 2024年6月17日 星期一 农历五月十二 1、 今年首个红色山洪灾害预警:17日,浙江西南部、福建北部局地发生山洪灾害可能性很大。 2、 国家医疗保障局重构产科服务价格项目,“分娩镇痛”亲情陪产…

AI大模型在运动项目的深度融合和在穿戴设备的实践及未来运动健康技术发展

文章目录 1. 技术架构2. 模型选择2.1 LSTM(长短期记忆网络)2.2 CNN(卷积神经网络)2.3 Transformer 3. 数据处理数据预处理 4. 实时性要求4.1 边缘计算4.2 模型优化 5. 数据隐私与安全6. 深入分析AI大模型在穿戴设备的应用和未来发…

Yum安装LAMP

查看当前80端口是否被占用 ss -tulanp | grep 80查询httpd是否在yum源中 yum info httpd安装httpd yum -y install httpd启动httpd服务,设置开机自启 systemctl enable httpd --now systemctl start httpd查看当前进程 ps aux | grep httpd查看当前IP&#xff…

Google谈出海:品牌「性价比」转向「心价比」

Google Marketing Live中国站活动现场 越来越多的中国全球化品牌基于对全球消费和海外地区的深刻洞察,不断提升产品研发和迭代能力,在海外消费者心中塑造「中国质造」和「中国智造」的新形象。2023年6月15日,凯度与Google合作发布《2023 凯…

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

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