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

【数据结构】单链表(笔记总结)

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:数据结构
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注


前景回顾

上期讲解了顺序表,虽然它的尾插和尾删的时间复杂度都是O(1),但还是存在一些缺陷的,比如中间和头部插入数据效率低下,还会存在一定的空间浪费等。现在我们来看看链表是否能解决顺序表的缺陷。

目录

  • 前景回顾
  • 一、概念
  • 二、链表的结构
  • 三、链表的分类
  • 四、链表的实现
      • 4.1 准备工作
      • 4.2 接口
      • 4.3 单链表之动态申请一个节点
      • 4.4 单链表之打印
      • 4.5 单链表之尾插
      • 4.6 单链表之头插
      • 4.7 单链表之尾删
      • 4.8 单链表之头删
      • 4.9 单链表之查找
      • 4.10 单链表之在pos之前插入x
      • 4.11 单链表之删除指定pos结点
      • 4.12 单链表之在pos位置之后插入x
      • 4.13 单链表之删除pos位置之后的结点
      • 4.14 单链表之结点释放
  • 五、总结

一、概念

链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接来实现的

二、链表的结构

  • 物理结构

在这里插入图片描述

  1. 物理结构就是数据在内存中实实在在的变化。
  2. 我们通常把一小块的内存空间称为结点,而显示中的结点一般都是从堆上申请的
  3. 链表之所以能链接起来,主要是因为上一个结点存储着下一个结点的地址
  4. 然而在平时做题的时候,不可能把图画的这么详细,因此引入了逻辑结构
  • 逻辑结构

在这里插入图片描述

逻辑结构是为了方便理解,形象画出来的。(一般分析都画逻辑结构)

三、链表的分类

实际中的链表的结构非常多样,以下情况组合起来就有8种结构:

1. 单向或者双向

在这里插入图片描述

2. 带头(哨兵位)或者不带头

在这里插入图片描述

带哨兵位的头结点是不存储有意义数据的

3. 循环或者非循环

在这里插入图片描述

总结
虽然有这么多的链表结构,但是最常用是以下这两种

  • 不带头单向非循环链表
    在这里插入图片描述
  • 带头双向循环链表
    在这里插入图片描述

四、链表的实现

4.1 准备工作

为了方便管理,我们可以创建多个文件来实现

  1. test.c - 测试代码逻辑 (源文件)
  2. SList.c - 动态的实现 (源文件)
  3. SList.h - 存放函数的声明 (头文件)
    在这里插入图片描述

4.2 接口

【SList.h】

typedef int SLTDateType;//定义结构体
typedef struct SListNode
{SLTDateType data; struct SListNode* next;//存储下一个结点的地址
}SListNode;// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
//单链表在pos之前插入x
void SListInsert(SListNode** plist,SListNode* pos,SLTDateType x);
//单链表删除指定pos结点
void SListErase(SListNode** plist,SListNode* pos);
//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
//结点释放
void SListDestroy(SListNode** plist);

4.3 单链表之动态申请一个节点

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("newnode :: malloc");return NULL;}newnode->next = NULL;newnode->data = x;return newnode;
}

其实这个接口可以不用写,写出来是因为后面的尾插、头插等需要向内存申请空间,然而为了减少代码量,因此就多了这个接口。

4.4 单链表之打印

// 单链表打印
void SListPrint(SListNode* plist)
{SListNode* cur = plist;while (cur) //cur != NULL{printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}

【笔记总结】

  1. 为什么不断言plist结点
    因为空链表是可以打印的。
  2. 为什么不直接拿plist遍历
    首先拿plist遍历肯定是没有问题的,但是为了保存头结点,最好还是新建一个结点遍历。
  3. cur = cur->next 千万不能写成 cur++
    原因是链表在内存空间上是不连续的。

4.5 单链表之尾插

void SListPushBack(SListNode** pplist, SLTDateType x)
{assert(pplist);//向内存申请空间SListNode* newnode = BuySListNode(x);//当空链表为空 -- 赋值if (*pplist == NULL){*pplist = newnode;}//不为空 -- 找到尾节点,再链接else{SListNode* tail = *pplist;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}

【笔记总结】

  1. 为什么要二级指针?
    首先,pplist一开始是指向头结点的,当pplist指向NULL时,尾插时就要改变头结点,如果不是二级指针,即使修改了头结点,尾插后,pplist还是指向NULL(不变)
  2. 为什么断言pplist,而不断言*pplist
    不断言*pplist是因为空链表可以尾插
    断言pplist是因为pplist其实存储的是*pplist的地址,即使*pplist == NULL,但pplist绝对不可能为NULL

4.6 单链表之头插

// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{assert(pplist);//向内存申请空间SListNode* newnode = BuySListNode(x);//头插newnode->next = *pplist;*pplist = newnode;}

【常见错误】
头插过程特别容易出错,少部分人可能会写成
*pplist = newnode
newnode->next = *pplist
如果这么写就大错特错了,原因是如果这么写,就找不到原来的头结点了
在这里插入图片描述

4.7 单链表之尾删

// 单链表的尾删
void SListPopBack(SListNode** pplist)
{assert(pplist);assert(*pplist); //空链表不能尾删//如果链表中只有一个结点,直接释放if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else{//尾插过程SListNode* prev = NULL;SListNode* tail = *pplist;//找尾结点while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);prev->next = NULL;}

【笔记总结】

  1. 要断言*pplist,因为空链表是不能尾删。
  2. 为什么还要定义prev
    首先,即使找到了尾结点,并把尾结点释放掉,但在尾结点释放之前,并没有把尾结点的前一个结点的next掷为NULL。所以定义prev是为了找到原尾结点的前一个结点。
    在这里插入图片描述

4.8 单链表之头删

// 单链表头删
void SListPopFront(SListNode** pplist)
{assert(pplist);assert(*pplist);//空链表不能头删SListNode* first = *pplist;*pplist = first->next;free(first);
}

【动图展示】

在这里插入图片描述

4.9 单链表之查找

// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{SListNode* cur = plist;while (cur){if (cur->data == x){return cur;}}//遍历完还是没找到return NULL;
}

4.10 单链表之在pos之前插入x

//单链表在pos之前插入x
void SListInsert(SListNode** plist, SListNode* pos, SLTDateType x)
{assert(plist);assert(pos);//开个新节点SListNode* newnode = BuySListNode(x);//如果pos指向头结点,相当于头插if (pos == *plist){SListPushFront(plist, x);}else{//找到pos前一个位置SListNode* prev = *plist;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}
}

【动图展示】

在这里插入图片描述

4.11 单链表之删除指定pos结点

//单链表删除指定pos结点
void SListErase(SListNode** plist, SListNode* pos)
{assert(plist);assert(pos);//pos指向头结点,相当于头删if (*plist == pos){SListPopFront(plist);}else{//找到pos前一个结点SListNode* prev = *plist;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);}
}

【动图展示】

在这里插入图片描述

4.12 单链表之在pos位置之后插入x

//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}

注意链接顺序,不要和头插犯同样的错误

【动图展示】
在这里插入图片描述

4.13 单链表之删除pos位置之后的结点

// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{assert(pos);assert(pos->next);//之后的结点不能为NULL//记录pos后一个结点SListNode* del = pos->next;//链接pos->next = del->next;//删除释放free(del);
}

【动图展示】

在这里插入图片描述

4.14 单链表之结点释放

//结点释放
void SListDestroy(SListNode** plist)
{SListNode* cur = *plist;while (cur){//在释放前记录下一个结点SListNode* next = cur->next;free(cur);cur = next;}*plist = NULL;
}

【动图展示】

在这里插入图片描述

五、总结

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持:O(N)
任意位置插入或者删除元素可能需要搬移元素,效率低O(N)只需修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁
缓存利用率

相关文章:

【数据结构】单链表(笔记总结)

👦个人主页:Weraphael ✍🏻作者简介:目前学习C和算法 ✈️专栏:数据结构 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞&…...

Git操作之 git add 撤销、git commit 撤销

1、git add 添加多余文件 撤销操作 git reset HEAD 后面什么都不跟的,就是上一次add 里面的内容全部撤销 git reset HEAD XXX 后面跟文件名,就是对某个文件进行撤销 2、git commit 撤销操作 git reset --soft HEAD^ 这样就成功的撤销了commit操作 注…...

用PyTorch实现MNIST数据集手写数字识别

资源下载:用Pytorch实现MNIST数据集的手写数字识别介绍资源-CSDN文库 手写数字识别是一项相当普遍的应用,因为在现实生活中,我们经常需要对手写数字进行识别,例如在邮政服务中,我们需要对邮件上的邮政编码进行识别&am…...

leetcode3:无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…...

ChatGPT让现在的软件都土掉渣了

我们家有两个娃,每次我们想要出去时订个酒店时都好麻烦。我在某程上找,我先看有没有家庭房,但家庭房很少,而且有些家庭房实际上只能睡得下两大一小。普通房间能不能睡得下四个人,那可是得查看很多信息,如床…...

IU5708D低静态电流同步升压DC-DC 控制器

IU5708D是高性能宽输入范围 (4.5V~40V) 同步升压控制器,支持高达52V的输出电压。输出电压采用恒定频率电流模式脉宽调制(PWM) 控制来实现调节。 芯片通过外部定时电阻器或通过与外部时钟信号同步来设置开关频率。在电阻编程模式下,开关频率可从50KHz编程…...

ubuntu查看软件安装路径

ubuntu怎么查看软件安装位置在哪 - 服务器 - 亿速云 1、执行程序查看 在终端使用type执行软件程序查看。 type google-chrome 2、通过进程查看对应的软件程序 在终端使用以下命令查看所有进程名。 ps -e 再使用以下过滤命令查看对应的进程信息即可。 ps aux|grep 软件名 …...

动态规划总结

1,01背包dp(每件物品最多选一次): 因为背包为0 的时候,什么都装不了,所以为零 ,就是他们的最优解。 最后一个单元格为最后的答案。 01背包模板 public class Knapsack {public static int kn…...

分享:数据库存储与索引技术(一)存储模型与索引结构演变

欢迎访问 OceanBase 官网获取更多信息:https://www.oceanbase.com/ 本文来自OceanBase社区分享,仅限交流探讨。原作者马伟,长期从事互联网广告检索系统的研发,对数据库,编译器等领域也有浓厚兴趣。 文章目录综述传统单…...

ZeusAutoCode代码生成工具(开源)

ZeusAutoCode代码生成工具 一、简介 Zeus代码生成器是一款自动代码生成工具,旨在快速生成基础的CRUD代码,在此基础上也提供了一些高级功能,做到灵活配置,生成可扩展性强的代码。 后端是基于springboot、freemarker、mybatisplu…...

算法题记录

力扣的算法题:1154 给你一个字符串 date ,按 YYYY-MM-DD 格式表示一个 现行公元纪年法 日期。返回该日期是当年的第几天。 示例 1: 输入:date “2019-01-09” 输出:9 解释:给定日期是2019年的第九天。 示例…...

章节2 行走数据江湖,只需一行代码

目录6. 函数填充,计算列6.1 excel操作6.2 pandas操作16.3 pandas操作28. 数据筛选、过滤,[绘图前的必备功课]8.1 excel操作8.2 Python操作http://sa.mentorx.net 蔓藤教育6. 函数填充,计算列 书的编号、书的名字、标价、折扣、最终价钱 最终…...

springboot集成xx-job;

概念理解: xx-job是一个分布式任务调度平台。比如你有AB两个项目。 AB的定时任务就要在xx-job上个注册。同时AB要配置对应的依赖。 所以集成xx-job要分2步骤:第一步:先搭建xx-job服务 第二步,在A项目中导包并引用。 第一步&am…...

35岁,失业6个月终于接到降薪offer:有面就面,薪酬不限,随机应变说瞎话,对奇葩面试官保持礼貌克制,为拿offer什么都能忍...

被裁后为了生存,人需要做出什么改变?一位35岁网友在失业6个月后终于拿到offer,虽然降薪到四年前的水平,但能继续养家糊口,楼主已经很满意了,并分享了自己的个人经验:1.挖掘历史项目经验&#xf…...

如何有效管理项目进度 都有哪些解决方法

项目进度管理是确保项目按时完成的关键因素之一。如果一个项目不能按时完成,那么它可能会导致成本超支、客户不满意和失去信誉等问题。因此,有效的项目进度管理至关重要。在本文中,我们将探讨如何有效管理项目进度以及可以采取哪些解决方法。…...

互联网随想(三) 光纤与电路交换

光纤的 “纤”,读 xian(先),第一声,而不是 qian(千)。 光纤之于通信,就像半导体之于计算机。光纤突破了通信的电子瓶颈,就像半导体集成电路突破了计算机的电子管瓶颈一样。 但本文不是赞美光纤的,本文为反…...

electron之旅(二)react使用

首先使用react模板 我们这里使用的是vite和yarn yarn create vite #创建vite的react-js模板初始化依赖 yarn添加依赖 state(状态管理) yarn add redux react-reduxroutes(react路由) yarn add react-router-domelectron依赖 yarn add electron vite-plugin-electron cross-env…...

ChatGPT基础知识系列之Prompt

ChatGPT基础知识系列之Prompt 在 ChatGPT 中,用户可以输入任何问题或者话题,如天气、体育、新闻等等。系统将这个输入作为一个“提示”(prompt)输入到 GPT 模型中进行处理。GPT 模型会基于其学习到的语言规律和上下文知识,生成一个自然语言回答,并返回给用户。 例如,当…...

SpringBoot3 - Spring Security 6.0 Migration

Spring Security 6.0 Migration https://docs.spring.io/spring-security/reference/5.8/migration/servlet/config.html 最近在做SpringBoot2.x到3.0的升级。其中最主要的一部分是javax -> jakartapackageName的变更,另外一部分是对一些废弃/删除的类进行替换。…...

【新2023Q2模拟题JAVA】华为OD机试 - 最少停车数

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:最少停车数 题目 特定大小的…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

GitHub 趋势日报 (2025年06月08日)

📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL

ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...

aardio 自动识别验证码输入

技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...