当前位置: 首页 > news >正文

探索数据结构:单链表的实战指南


✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法
贝蒂的主页:Betty‘s blog

前言

在上一章节中我们讲解了数据结构中的顺序表,知道了顺序表的空间是连续存储的,这与数组非常类似,为我们随机访问数据提供了便利的条件。但是同时当插入数据时可能存在移动数据与扩容的情况,这大大增加我们的时间与空间成本。为了解决这个问题,就要学习我们今天要讲解的链表。

1. 什么是链表

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。与顺序表不同,链表的存储数据在内存是随机分布的。

2. 链表的分类

链表的种类多种多样,其中最常见的有八种,它们大致可以分为三类:

2.1 单向或者双向

2.2 带不带头节点

2.3 循环不循环

本专栏将选择其中两种常见链表进行讲解,那就是无头单向非循环链表与与带头双向循环链表,这两种优点主要有:

  • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  • 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

3. 单链表的功能

单链表的主要功能有以下几个:

  1. 打印单链表中的数据。
  2. 对单链表进行头插(开头插入数据)。
  3. 对单链表进行头删(开头删除数据)。
  4. 对单链表进行尾插(末尾插入数据)。
  5. 对单链表进行尾删(末尾删除数据)。
  6. 对单链表进行查找数据。
  7. 对单链表数据进行修改。
  8. 任意位置的删除和插入数据。
  9. 销毁单链表。

4. 单链表的定义

单链表的结点定义方式与我们以往定义的方式都不同,它是一个结构体中包含两个成员。一个是存储数值,一个存放下一个结点的地址。

typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SListNode;

实际内存存储结构:

5. 单链表功能实现

5.1 打印单链表

打印链表需要对函数传参指向链表的指针,首先为了保证链表不为空(NULL),需要对其进行断言。

(1) 代码实现
void PrintSList(SListNode* plist)
{SListNode* cur = plist;while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}
(2) 复杂度分析
  • 时间复杂度:因为需要遍历整个链表,显然时间复杂度为O(N)。
  • 空间复杂度:打印链表不需要格外的空间,所以空间复杂度为O(1).

5.2 单链表头插

(1) 创建结点

单链表每次插入都需要插入一个节点,所以我们最好写一个创建节点的函数方便后续调用。

SListNode* SListCreatNode(SLTDataType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc fail:");return;}newnode->data = x;newnode->next = NULL;return newnode;
}
(2) 头插代码实现

单链表的头插与顺序表头插最大的不同就是单链表传参需要二级指针。因为头插数据需要改变一级指针plist的指向,而形参的改变无法影响实参,所以需要二级指针才能改变一级指针plist的指向。

void SListPushFront(SListNode** plist, SLTDataType x)
{assert(plist);SListNode* newnode = SListCreatNode(x);newnode->next = *plist;*plist = newnode;
}
(3) 复杂度分析
  • 时间复杂度:不需要额外时间的消耗,时间复杂度为O(1)。
  • 空间复杂度:固定创造一个节点,空间复杂度为O(1)。

5.3 单链表头删

头删与头插类似,都可能需要改变plist的指向,所以也需要二级指针。并且也需要防止链表为空的情况发生。

(1) 代码实现
void SListPopFront(SListNode** plist)
{assert(plist);assert(*plist);SListNode* cur = (*plist)->next;free(*plist);*plist = cur;
}
(2) 复杂度分析
  • 时间复杂度:不需要额外时间的消耗,时间复杂度为O(1)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

5.4 单链表尾插

当链表为空时,单链表尾插就会变成单链表头插,也需要改变plist的指向。其余情况即可通过循环寻找尾节点。

(1) 代码实现
void SListPushBack(SListNode** plist, SLTDataType x )
{assert(plist);if (*plist== NULL){*plist = SListCreatNode(x);}else{SListNode* ptail = *plist;while (ptail->next){ptail = ptail->next;}ptail->next = SListCreatNode(x);}
}
(2) 复杂度分析
  • 时间复杂度:需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:固定创造一个节点,空间复杂度为O(1)。

5.5 单链表尾删

与单链表尾插类似,当链表只有一个头结点时需要将plist置为空,所以也需要二级指针。并且也需要防止链表为空的情况发生。

(1) 代码实现
void SListPopBack(SListNode** plist)
{assert(plist);assert(*plist);//防止链表为空if ((*plist)->next == NULL){free(*plist);*plist = NULL;}else{SListNode* ptail = *plist;while (ptail->next->next){ptail = ptail->next;}free(ptail->next);ptail->next = NULL;}	
}
(2) 复杂度分析
  • 时间复杂度:需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

5.6 单链表的查找

如果找到就返回这个节点的地址,否则就返回空指针NULL

(1) 代码实现
SListNode* SListSearch(SListNode* plist, SLTDataType x)
{assert(plist);SListNode* cur = plist;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
(2) 时间复杂度
  • 时间复杂度:可能需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

5.6 单链表的修改

单链表的修改可以与查找配套使用。

(1) 代码实现
void SListModify(SListNode*plist, SListNode* pos, SLTDataType x)
{assert(plist);assert(pos);SListNode* cur = plist;while (cur){if (cur == pos){cur->data = x;break;}cur = cur->next;}
}
(2) 复杂度分析
  • 时间复杂度:可能需要遍历整个链表,时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

5.7 单链表任意位置的插入

(1) 代码实现

某个位置之前插入,当链表为空或者在头节点插入时使用头插。

void SListInsertF(SListNode** plist, SListNode* pos, SLTDataType x)
{assert(plist);assert(pos);if (*plist == pos || *plist == NULL){SListPushFront(plist, x);//头插}else{SListNode* newnode = SListCreatNode(x);SListNode* prev = *plist;while (prev->next != pos){prev = prev->next;}newnode->next = pos;prev->next = newnode;}
}

某个位置之后插入,当链表为空或者在尾节点插入时使用尾插。

void SListInsertB(SListNode** plist, SListNode* pos, SLTDataType x)
{assert(plist);assert(pos);if (*plist == NULL||pos->next==NULL){SListPushBack(plist, x);//尾插}else{SListNode* tmp = pos->next;SListNode* newnode = SListCreatNode(x);pos->next = newnode;newnode->next = tmp;}
}
(2) 复杂度分析
  • 时间复杂度:无论向前插入还是向后插入都可能需要遍历整个链表,所以时间复杂度为O(N)。
  • 空间复杂度:固定创造一个节点,空间复杂度为O(1)。

5.8 任意位置的删除

任意位置的删除需要提前保存好前一个节点与后一个节点的位置。

(1) 代码实现
void SListErase(SListNode** plist, SListNode* pos)
{assert(plist);assert(*plist);assert(pos);if (*plist == pos){SListPopFront(plist);//头删}SListNode* prev = *plist;while (prev->next!=pos){prev = prev->next;}SListNode* tmp = pos->next;prev->next = tmp;free(pos);pos = NULL;
}
(2) 复杂度分析
  • 时间复杂度:可能需要遍历整个链表,所以时间复杂度为O(N)。
  • 空间复杂度:固定创造一个节点,空间复杂度为O(1)。

5.9 销毁链表

销毁链表需要依次遍历释放节点。

(1) 代码实现
void SListDestroy(SListNode** plist)
{assert(plist);assert(*plist);SListNode* cur = *plist;while (cur){SListNode* tmp = cur->next;free(cur);cur =tmp;}*plist = NULL;//防止野指针
}
(2) 复杂度分析
  • 时间复杂度:需要遍历整个链表,所以时间复杂度为O(N)。
  • 空间复杂度:不需要格外的空间消耗,空间复杂度为O(1)。

相关文章:

探索数据结构:单链表的实战指南

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:数据结构与算法 贝蒂的主页:Betty‘s blog 前言 在上一章节中我们讲解了数据结构中的顺序表,知道了顺序…...

短视频矩阵系统----矩阵系统源码搭建(技术门槛?)

短视频矩阵是什么意思?短视频矩阵的含义可以理解为全方位的短视频账号,通过不同的账号实现全方位的品牌展示。实际上是指一个短视频账号,通过不同的链接实现品牌展示,在不同的粉丝流量账号中互相转发同一个品牌,在主账…...

Spring事务注解@Transactional的流程和源码分析

Spring事务简介 Spring事务有两种方式: 编程式事务:编程式事务通常使用编程式事务管理API实现,比如Spring提供的PlatformTransactionManager接口,使用它手工编码去操控事务。声明式事务:注解式事务使用AOP&#xff0…...

在别的地方下载的二次封装Windows镜像怎么安装?GHO镜像详细安装教程

前言 在系统之家或者其他地方下载的镜像文件怎么装到电脑上? 首先要知道系统之家下载的Windows镜像文件基本上都是.iso结尾的,要进入到对应镜像包才能看出系统镜像是什么格式。 如何分辨镜像的格式 选择对应的.iso镜像,点击【鼠标右键】-【装…...

使用Lerna + Yarn Workspace管理Monorepo项目

1.前言 通常,我们会根据自身业务的实际情况,将通用的组件、逻辑等提取成NPM包,方便以后复用。但这些提取出来的NPM包可能互相之间存在依赖,如果仍然采用 Multirepo 的形式进行管理,则在包的版本管理、依赖管理、调试等…...

如何将gzip后缀压缩包重命名任意后缀名并依然通过gzip.open()读取压缩包文件内容

在 Python 中,gzip.open() 用于解压缩 .gz 后缀的文件。因此,如果您将文件的后缀从 .gz 更改为其他后缀,例如 .diy,然后尝试使用 gzip.open() 打开它,会导致失败,因为 Python 会尝试使用 gzip 解压缩它&…...

C语言从入门到精通 第十一章(文件操作)

写在前面: 本系列专栏主要介绍C语言的相关知识,思路以下面的参考链接教程为主,大部分笔记也出自该教程。除了参考下面的链接教程以外,笔者还参考了其它的一些C语言教材,笔者认为重要的部分大多都会用粗体标注&#xf…...

安装安卓studio无法下载sdk解决方法

安装安卓studio无法下载sdk的解决方法如下: 因为google被墙了,android sdk无法下载。 只要修改host文件,就可以下载sdk了。host文件的位置在:C:\Windows\System32\drivers\etc\hosts host文件添加如下内容: #google_…...

express+mysql+vue,从零搭建一个商城管理系统10--添加商品

提示:学习express,搭建管理系统 文章目录 前言一、新建models/goods.js二、新建routes/goods.js三、添加goods表四、添加商品总结 前言 需求:主要学习express,所以先写service部分 一、新建models/goods.js models/goods.js con…...

java实现大文件的分割与合并

最近遇到一个问题,某网盘上传文件时,文件大小超过了4个G ,不能上传,所以就想到了利用的java的IO流,将文件分割成多个小文件,上传到网盘上,等到需要用的时候,下载下来然后再进行文件的…...

【计网】TCP协议安全与风险:深入探讨网络通信的基石

🍎个人博客:个人主页 🏆个人专栏:Linux ⛳️ 功不唐捐,玉汝于成 目录 🌐前言 🔒正文 TCP (Transmission Control Protocol): UDP (User Datagram Protocol): HTTP (Hypertext Transfer …...

苹果App Store上架工具介绍

文章目录 摘要引言正文1. Xcode2. [appuploder](https://www.applicationloader.net/)3. [克魔助手](https://keymob.com/) 4.[ipa guard](https://www.ipaguard.com/)总结参考资料 摘要 苹果App Store作为iOS应用程序的主要分发渠道,上架应用程序需要遵守规定和通…...

TCP重传机制、滑动窗口、拥塞控制

一、总述 TCP,Transmission Control Protocol,是一个面向连接、基于流式传输的可靠传输协议,考虑到的内容很多,比如数据包的丢失、损坏、分片和乱序等,TCP协议通过多种不同的机制来实现可靠传输。今天,重点…...

electron+vue3全家桶+vite项目搭建【29】封装窗口工具类【3】控制窗口定向移动

文章目录 引入实现效果思路声明通用的定位对象主进程模块渲染进程测试效果 引入 demo项目地址 窗口工具类系列文章: 封装窗口工具类【1】雏形 封装窗口工具类【2】窗口组,维护窗口关系 封装窗口工具类【3】控制窗口定向移动 很多时候,我们想…...

深入了解304缓存原理:提升网站性能与加载速度

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

python-批量操作excel

批量新增excel文件 import osimport xlwings as xwapp xw.App(visibleTrue,add_bookFalse)#visible设置为ture的时候会自动打开创建的excel文件,设为为false的时候不会看到excel文件打开了,实际进程占用了....dept_list [人事部,财务部,研发部,行政部…...

#QT(串口助手-界面)

1.IDE:QTCreator 2.实验:编写串口助手 3.记录 接收框:Plain Text Edit 属性选择:Combo Box 发送框:Line Edit 广告:Group Box (1)仿照现有串口助手设计UI界面 (2)此时串口助手大…...

C语言进阶——位段

在C语言中,位段(Bit Fields)是一种用来对结构体中的成员进行位级别的控制的特性。通过位段,我们可以灵活地控制结构体中各个成员的位数,从而节省内存空间并提高程序的效率。本篇博客将详细讲解C语言中位段的相关知识&a…...

软件设计师软考题目解析23 --每日五题

想说的话:要准备软考了。0.0,其实我是不想考的,但是吧,由于本人已经学完所有知识了,只是被学校的课程给锁在那里了,不然早找工作去了。寻思着反正也无聊,就考个证玩玩。 本人github地址&#xf…...

总结:前后端集合、数组类型数据交互底层原理,SpringBoot框架解析

总结:前后端集合、数组类型数据交互底层原理,SpringBoot框架解析 一前后端信息交互本质:1.两台电脑可以通过收发电磁波、控制网线电路开关等基础物理设施,就可以进行物理层面的电信号交互,电信号又可以通过各种传感设备…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...