数据结构-单链表

数据结构-单链表

单链表

概念与结构

概念:链表是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
逻辑结构:线性
物理结构(存储结构):不一定是线性的

链表就类似一个火车,车头是哨兵位(可有可无),车厢是节点

在这里插入图片描述
  • 将火车里的某节车厢去掉或加上,不会影响其他车厢,每节车厢都是独立存在的。

在链表⾥,每节“车厢”是什么样的呢? \color{red}{在链表⾥,每节“车厢”是什么样的呢?} 在链表⾥,每节“车厢”是什么样的呢?

在这里插入图片描述

结点

与顺序表不同的是,链表⾥的每节"车厢"都是独立申请下来的空间,我们称之为“结点/结点结点的组成主要有两个部分:当前结点要保存的数据和保存下⼀个结点的地址(指针变量链表中每个结点都是独立申请的(即需要插入数据时才去申请一块结点的空间),我们需要通过指针变量来保存下一个结点位置才能从当前结点找到下一个结点。

链表的性质

1、链式机构在逻辑上是连续的,在物理结构上不一定连续
2、结点⼀般是从堆上申请的
3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

结合前⾯学到的结构体知识,我们可以给出每个结点对应的结构体代码:
假设当前保存的结点为整型:

structSListNode{int data;//结点数据structSListNode* next;//指针变量⽤保存下⼀个结点的地址};

当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数据,也需要保存 \color{green}{当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数据,也需要保存} 当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数据,也需要保存
下⼀个结点的地址(当下⼀个结点为空时保存的地址为空) \color{green}{下⼀个结点的地址(当下⼀个结点为空时保存的地址为空)} 下⼀个结点的地址(当下⼀个结点为空时保存的地址为空)
当我们想要从第⼀个结点走到最后⼀个结点时,只需要在当前结点拿上下⼀个结点的地址就可以了。 \color{green}{当我们想要从第⼀个结点走到最后⼀个结点时,只需要在当前结点拿上下⼀个结点的地址就可以了。} 当我们想要从第⼀个结点走到最后⼀个结点时,只需要在当前结点拿上下⼀个结点的地址就可以了。

链表的打印

在这里插入图片描述

实现单链表

头文件

  • SList.h
#include<stdio.h>#include<stdlib.h>#include<assert.h>//链表的结构typedefint SLTDataType;typedefstructSListNode{ SLTDataType data;structSListNode* next;//指向下一个节点的地址}SLTNode;//typedef struct SListNode SLTNode;voidSLTPrint(SLTNode* phead);//尾插voidSLTPushBack(SLTNode** pphead, SLTDataType x);//头插voidSLTPushFront(SLTNode** pphead, SLTDataType x);//尾删voidSLTPopBack(SLTNode** pphead);//头删voidSLTPopFront(SLTNode** pphead);//查找 SLTNode*SLTFind(SLTNode* phead, SLTDataType x);//在指定位置之前插⼊数据voidSLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//在指定位置之后插入数据voidSLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos结点voidSLTErase(SLTNode** pphead, SLTNode* pos);//删除pos之后的结点voidSLTEraseAfter(SLTNode* pos);//销毁链表voidSListDestroy(SLTNode** pphead);

源文件

  • SList.c

单链表的打印

voidSLTPrint(SLTNode * phead){ SLTNode* pcur = phead;while(pcur !=NULL){printf("%d -> ", pcur->data); pcur = pcur->next;}printf("NULL\n");}

单链表申请新节点内存

SLTNode*SLTBuyNode(SLTDataType x){ SLTNode* newnode =(SLTNode*)malloc(sizeof(SLTNode));if(newnode ==NULL){perror("malloc fail!");exit(1);} newnode->data = x; newnode->next =NULL;return newnode;}
这里是申请新节点,并且把对应的数据存入节点中,所以接下来只需要将各个节点连接起来(链表中的指针)

尾插

//尾插voidSLTPushBack(SLTNode** pphead, SLTDataType x){assert(pphead);//申请新节点 SLTNode* newnode =SLTBuyNode(x);//链表为空if(*pphead ==NULL){*pphead = newnode;}else{ SLTNode* ptail =*pphead;while(ptail->next !=NULL){ ptail = ptail->next;}//找到了尾结点 ptail newnode ptail->next = newnode;}}
传址调用,SLTNode** pphead,不然无法修改SLTNode* phead结构体指针需要做出特殊判断,当链表为空的时候

头插

//头插voidSLTPushFront(SLTNode** pphead, SLTDataType x){assert(pphead); SLTNode* newnode =SLTBuyNode(x);//newnode *pphead newnode->next =*pphead;*pphead = newnode;}

尾删

//尾删voidSLTPopBack(SLTNode** pphead){//链表为空不能删除assert(pphead &&*pphead);//链表只有一个节点的情况if((*pphead)->next ==NULL){free(*pphead);*pphead =NULL;}else{ SLTNode* prev =NULL; SLTNode* ptail =*pphead;while(ptail->next){ prev = ptail; ptail = ptail->next;}//prev ptail prev->next =NULL;free(ptail); ptail =NULL;}}
处理特殊情况,链表只有一个节点的情况链表为空不能删除

头删

//头删voidSLTPopFront(SLTNode** pphead){assert(pphead &&*pphead); SLTNode* next =(*pphead)->next;free(*pphead);*pphead = next;}

查找

//查找 SLTNode*SLTFind(SLTNode* phead, SLTDataType x){ SLTNode* pcur = phead;while(pcur){if(pcur->data == x){return pcur;} pcur = pcur->next;}//未找到returnNULL;}
不需要传二级指针了

在指定位置之前插入数据

//在指定位置之前插⼊数据voidSLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x){assert(pphead && pos); SLTNode* newnode =SLTBuyNode(x);//pos指向头结点if(pos ==*pphead){//头插SLTPushFront(pphead, x);}else{//找pos的前一个节点 SLTNode* prev =*pphead;while(prev->next != pos){ prev = prev->next;}//prev newnode pos prev->next = newnode; newnode->next = pos;}}
pos不能为空指针特殊情况,pos指向头结点

在指定位置之后插入数据

//在指定位置之后插入数据voidSLTInsertAfter(SLTNode* pos, SLTDataType x){assert(pos); SLTNode* newnode =SLTBuyNode(x);//pos newnode pos->next newnode->next = pos->next; pos->next = newnode;}

删除pos结点

//删除pos结点voidSLTErase(SLTNode** pphead, SLTNode* pos){assert(pphead && pos);//pos刚好就是头结点——头删if(pos ==*pphead){SLTPopFront(pphead);}else{ SLTNode* prev =*pphead;while(prev->next != pos){ prev = prev->next;}//prev pos pos->next prev->next = pos->next;free(pos); pos =NULL;}}
pos不能为空指针特殊情况,pos刚好就是头结点—头删

删除pos之后的结点

//删除pos之后的结点voidSLTEraseAfter(SLTNode* pos){assert(pos && pos->next);//pos del del->next SLTNode* del = pos->next; pos->next = del->next;free(del); del =NULL;}

销毁链表

//销毁链表voidSListDestroy(SLTNode** pphead){assert(pphead); SLTNode* pcur =*pphead;while(pcur){ SLTNode* next = pcur->next;free(pcur); pcur = next;}*pphead =NULL;}

链表的分类

链表的结构⾮常多样,以下情况组合起来就有 8 种( 2 x 2 x 2 )链表结构 \color{green}{链表的结构⾮常多样,以下情况组合起来就有8种(2 x 2 x 2)链表结构} 链表的结构⾮常多样,以下情况组合起来就有8种(2x2x2)链表结构

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

代码地址

仓库

感谢大佬们三连,你的鼓励就是对我创作的激励!!! \color{purple}{感谢大佬们三连,你的鼓励就是对我创作的激励!!!} 感谢大佬们三连,你的鼓励就是对我创作的激励!!!

在这里插入图片描述

Read more

【前端进阶之旅】50 道前端超难面试题(2026 最新版)|覆盖 HTML/CSS/JS/Vue/React/TS/ 工程化 / 网络 / 跨端

【前端进阶之旅】50 道前端超难面试题(2026 最新版)|覆盖 HTML/CSS/JS/Vue/React/TS/ 工程化 / 网络 / 跨端

文章目录 * 前言 * 一、原生开发(HTML/CSS/JavaScript) * 二、框架核心(Vue2/3、React16/18/19) * 三、网络协议 * 四、工程化 * 五、跨端开发(uniapp、uniappX) * 六、TypeScript * 写在最后 前言 作为前端开发者,想要突破中高级面试瓶颈,仅掌握基础语法远远不够 —— 大厂面试更侧重底层原理、手写实现、场景分析与跨领域综合能力。本文整理了50 道无答案版前端超难面试题,覆盖原生开发、框架核心、网络协议、工程化、跨端开发、TypeScript 六大核心方向排序且聚焦高频难点,适合自测、复盘或作为面试出题参考,建议收藏反复琢磨! 一、原生开发(HTML/CSS/JavaScript) 原生能力是前端的根基,

By Ne0inhk

星露谷农场规划器技术架构深度解析:从Node.js后端到SVG前端渲染的完整实现

星露谷农场规划器技术架构深度解析:从Node.js后端到SVG前端渲染的完整实现 【免费下载链接】stardewplannerStardew Valley farm planner 项目地址: https://gitcode.com/gh_mirrors/st/stardewplanner 星露谷农场规划器作为一个基于Node.js和现代前端技术栈构建的开源游戏规划工具,其技术架构融合了Express框架的API服务、Snap.svg的矢量图形渲染以及复杂的地形约束算法。该项目不仅为《星露谷物语》玩家提供了直观的农场设计体验,更在游戏工具开发领域展示了完整的技术实现路径。 技术架构核心模块分析 Node.js后端服务架构 项目采用Express.js作为核心Web框架,构建了一个完整的RESTful API服务。从index.js的入口文件可以看出,系统架构包含以下关键组件: * 中间件配置:body-parser支持25MB的大文件上传,满足复杂农场数据的传输需求 * 静态资源服务:通过express.static提供CSS、JavaScript和图片资源的托管

By Ne0inhk
《C#上位机开发从门外到门内》3-5:基于FastAPI的Web上位机系统

《C#上位机开发从门外到门内》3-5:基于FastAPI的Web上位机系统

文章目录 * 一、项目概述 * 二、系统架构设计 * 三、前后端开发 * 四、数据可视化 * 五、远程控制 * 六、系统安全性与稳定性 * 七、性能优化与测试 * 八、实际应用案例 * 九、结论 随着互联网技术的快速发展,Web上位机系统在工业自动化、智能家居、环境监测等领域的应用日益广泛。基于FastAPI或Flask的Web上位机系统,凭借其高效、灵活和易于扩展的特点,成为当前研究和应用的热点。本文将详细探讨基于FastAPI和Flask的Web上位机系统的设计与实现,涵盖系统架构、前后端开发、数据可视化、远程控制、安全性、性能优化以及实际应用案例等方面,旨在为相关领域的研究人员和工程技术人员提供参考和借鉴。 一、项目概述 Web上位机系统是一种通过网络实现对远程设备或环境进行实时监控和控制的系统。其核心目标是通过高效的数据传输和处理,确保监控的实时性和准确性,从而实现对远程设备的有效管理和控制。基于FastAPI或Flask的Web上位机系统利用Python的Web框架,通过互联网或局域网实现数据的传输和通信,具有广泛的应用前景。 Fa

By Ne0inhk
openclaw喂饭教程!在 Linux 环境下快速完成安装、初始化与 Web UI 配置

openclaw喂饭教程!在 Linux 环境下快速完成安装、初始化与 Web UI 配置

前言 OpenClaw 是一款开源的 AI Agent 工具,但对第一次接触的用户来说,完整跑通流程并不直观。本文以 Linux 环境为例,详细记录了 OpenClaw 的安装、初始化流程、模型选择、TUI 使用方式,以及 TUI 与 Web UI 认证不一致导致的常见问题与解决方法,帮助你最快速度把 OpenClaw 真正跑起来 环境准备 1)安装nodejs curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs > node

By Ne0inhk