数据结构-单链表

数据结构-单链表

单链表

概念与结构

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

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

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

在链表⾥,每节“车厢”是什么样的呢? \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

Flutter 三方库 git_hooks 鸿蒙强干预研发质量审核截断防线设防适配解析:依托钩子拦截引擎封锁全域代码递交链路建立极强合规化审计审查防火墙斩断-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 git_hooks 鸿蒙强干预研发质量审核截断防线设防适配解析:依托钩子拦截引擎封锁全域代码递交链路建立极强合规化审计审查防火墙斩断-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 git_hooks 鸿蒙强干预研发质量审核截断防线设防适配解析:依托钩子拦截引擎封锁全域代码递交链路建立极强合规化审计审查防火墙斩断技术债堆砌 前言 在 OpenHarmony 的大规模团队协作中,代码质量是团队的生命线。如果没有有效的约束,不符合规范的代码(甚至是无法通过静态分析的代码)会轻易地通过 git commit 进入代码库,导致 CI 构建频繁失败。git_hooks 库为 Flutter 开发者提供了一种轻量级的脚本化方案,可以在 Git 的关键生命周期(如提交前、推送前)自动运行检查。本文将带大家在鸿蒙端实战适配该库,夯实自动化工程的地基。 一、原直线性 / 概念介绍 1.1 基础原理/概念介绍 git_hooks 的核心逻辑是基于 Git

By Ne0inhk
飞书机器人与Claude Code交互:从手机指令到AI处理的全自动流程

飞书机器人与Claude Code交互:从手机指令到AI处理的全自动流程

飞书机器人与Claude Code交互:从手机指令到AI处理的全自动流程 * 一、背景 * 二、实现方案概览 * 三、操作步骤 * 前置准备 * 第一步:创建并进入Claude Code容器 * 配置Claude Code使用本地模型 * 测试Claude Code是否正常工作 * 第二步:安装Python依赖 * 第三步:获取飞书应用的凭证 * 第四步:编写并运行中间件脚本 * 脚本解释 * 运行脚本 * 第五步:在飞书中与机器人对话 * 常见问题 * 总结 一、背景 在日常开发中,我们经常需要快速查询代码问题、生成文档或执行简单的编程任务。如果有一款AI助手能随时响应,就像在电脑终端前一样,那该多方便!本教程将演示如何搭建一个飞书机器人,当你在手机飞书App上发送消息时,该消息会传递给运行在电脑上的Claude Code(一个智能编码助手),Claude Code处理后将结果回复到你的飞书会话中。 通过这个方案,你可以: * 在手机上随时向AI提问编程问题。 * 让AI帮你调试

By Ne0inhk

WebODM终极指南:免费开源无人机影像处理全流程详解

WebODM终极指南:免费开源无人机影像处理全流程详解 【免费下载链接】WebODMUser-friendly, commercial-grade software for processing aerial imagery. 🛩 项目地址: https://gitcode.com/gh_mirrors/we/WebODM WebODM是一款功能强大的开源无人机影像处理软件,能够将普通的航拍照片转化为专业级的地理空间数据产品。无论你是无人机爱好者、测绘工程师还是项目管理人员,这款工具都能帮助你快速生成正射影像图、三维模型和数字地形图,为各种应用场景提供精确的数据支持。 项目亮点速览 核心优势一览表 | 优势类别 | 具体特点 | 用户收益 | |---------|----------|----------| | 技术能力 | 自动化影像处理、多格式输出 | 无需专业知识即可操作 | | 成本效益 | 完全免费开源、无授权费用 | 大幅降低项目成本 | | 易用性 | 直观操作界面、跨平台兼容 | 快速上手零门槛 | | 扩展性 | 插件化架构、分布式处理 | 满足个性化需

By Ne0inhk

PRIDE-PPPAR 安装与配置完整指南

PRIDE-PPPAR 安装与配置完整指南 【免费下载链接】PRIDE-PPPARAn open‑source software for Multi-GNSS PPP ambiguity resolution 项目地址: https://gitcode.com/gh_mirrors/pr/PRIDE-PPPAR 项目概述 PRIDE-PPPAR 是一款由武汉大学GNSS研究中心开发的开源多GNSS(全球导航卫星系统)处理软件,专注于实现PPP(精确点定位)中的模糊度快速解算。该软件采用Fortran作为主要编程语言,辅以Shell脚本和少量C代码,旨在为科研人员和专业人士提供高精度的地理测量和地球物理应用解决方案。 核心技术特性 * 多频多星座GNSS数据处理:支持GPS、GLONASS、Galileo、北斗(BDS-2/3)以及QZSS信号 * 全频率PPP-AR技术:在任意双频电离层自由组合上进行模糊度固定 * 高动态处理能力:适用于飞行摄影测量、舰载重力测量等场景 * 先进的时钟估计和天线偏移模型:支持时间频率转移与高级大气建模 * 最新IGS标准支持:采

By Ne0inhk