发布时间:2026/9/5 0:25:04
零基础小白在学习链表(LinkedList)的思考 带你拆解底层逻辑 在数据结构中我们知道顺序表虽然尾插的时间复杂度O(1),但其无论是中间/头部的插⼊删除时间复杂度O(N)),还是增容时所面临空间的消耗都不可忽视思考我们当如何解决以上问题呢一、什么是链表其与数组的区别是什么1、链表一种物理上不连续但数据元素的逻辑顺序通过链表中指针次序所链接的存储结构2、其与数组的区别对比维度数组顺序表单链表无尾指针内存存储物理内存连续内存碎片化、不连续访问方式支持arr[i] 直接取O(1)不支持必须从头遍历O(n)头部插入/删除O(n)所有元素后移O(1)仅修改指针指向尾部插入均摊O(1)偶尔扩容拷贝O(n)每次要遍历到末尾中间插入/删除O(n)后续元素整体移位O(n)需要先找到目标位置空间开销仅存储数据额外开销小每个节点多存1个next指针额外内存开销大容量大小固定/动态扩容扩容有性能损耗无固定上限按需申请节点无需扩容3、链表结构(以单链表SLT [Single Linked Table]为例#includestdio.h#includestdlib.h// 1. 定义节点结构体structSLTNode{intdata;structSLTNode*next;};// 2. 给结构体起别名typedefstructSLTNodeNode;// 3. 给结构体指针起别名简化代码typedefstructSLTNode*PNode;采用struct关键字定义链表节点结构体节点分为两部分数据域 int data用于存储链表需要保存的数据可按需更换数据类型指针域 struct SListNode* next一级结构体指针存储下一个相邻节点的内存地址通过地址串联所有节点形成链式结构。使用typedef对结构体及结构体指针重命名Node等价于 struct SLTNode代表单个节点实体PNode等价于 struct SLTNode*代表节点指针简化链表操作代码书写。单链表逻辑特征节点内存不连续仅依靠next指针维系前后节点关系尾部节点next指针置NULL代表链表结束。二、链表的基本功能代码实现1、工具函数新建节点PNodeBuyNode(intx){PNode newNode(PNode)malloc(sizeof(Node));if(newNodeNULL){perror(malloc failed);exit(-1);}newNode-datax;newNode-nextNULL;returnnewNode;}malloc动态开辟内存初始化数据next 置空所有插入操作都会复用这个函数注意事项这里的newNode的类型是一级指针PNode即newNode存有对应节点的地址其指向一个节点2、打印链表遍历查看数据调试必备voidSLTPrint(PNode phead){PNode curphead-next;//跳过哨兵头节点while(cur!NULL){printf(%d - ,cur-data);curcur-next;}printf(NULL\n);}这里phead是一个不存有数据的指针(哨兵头节点)通过phead-next找到链表中第一个存有数据的有效节点通过cur指针不断将节点的数据打印而出并且将cur不断赋值成其所指向的下一节点的指针直至cur变为空指针 打印截止3、头插在链表最前面插入数据voidSLTPushFront(Node**pphead,LinkData x){Node*newnodeBuyNode(x);newnode-next*pphead;*ppheadnewnode;}错误写法// 错误写法无法修改外部头指针voidSLTPushFront(Node*phead,LinkData x){Node*newnodeBuyNode(x);newnode-nextphead;pheadnewnode;// 只修改函数内局部副本外部原指针完全不变}C 语言函数传参属于值拷贝传递如果头插函数仅使用一级指针Node* phead作形参当head为空指针时调用函数headNULL的值被拷贝到形参phead中此时phead也是NULL此时虽然他们的值都是NULL,但地址却是不一样的比如下方中的phead值为NULL 存有地址0x0003 自己地址为0x0002传入后会出现新的phead值NULL 存有地址0x0003但其地址可能时0x00A34、尾插在链表末尾插入数据(原理与3差不多) ps这里的ptail为为了找到原链表的尾节点不直接用*pphead为了便于寻找的同时不改动头节点voidSLTPushBack(Node**pphead,LinkData x){Node*newnodeBuyNode(x);Node*ptail*pphead;if(*ppheadNULL){*ppheadnewnode;}else{while(ptail-next){ptailptail-next;}ptail-nextnewnode;}}5、其余本质上实现原理与之前一致这里简要概述// 头删voidSLTPopFront(Node**pphead);// 尾删voidSLTPopBack(Node**pphead);// 按值查找节点Node*SLTFind(Node*phead,LinkData x);// 在pos节点前插入数据voidSLTInsert(Node**pphead,LinkData x,Node*pos);// 在pos节点后插入数据voidSLTInsertAfter(Node*pos,LinkData x);// 释放整条链表并置空头指针voidSLTDestroy(Node**pphead);各功能实现与简短说明5.1. 头删 SLTPopFrontvoidSLTPopFront(Node**pphead){Node*del*pphead;*ppheaddel-next;free(del);}说明可能修改头指针使用二级指针直接更新表头并释放原首节点。5.2. 尾删 SLTPopBackvoidSLTPopBack(Node**pphead){// 链表仅有一个节点if((*pphead)-nextNULL){free(*pphead);*ppheadNULL;return;}// 找到倒数第二个节点Node*cur*pphead;while(cur-next-next)curcur-next;free(cur-next);cur-nextNULL;}说明链表只剩单个节点时需要置空头指针必须二级指针。5.3. 查找 SLTFindNode*SLTFind(Node*phead,LinkData x){Node*curphead;while(cur){if(cur-datax)returncur;curcur-next;}returnNULL;}说明仅遍历读取数据不修改表头一级指针即可。5.4. 指定节点前插入 SLTInsertvoidSLTInsert(Node**pphead,LinkData x,Node*pos){Node*newnodeBuyNode(x);// 插入位置为头部更新表头if(*ppheadpos){newnode-next*pphead;*ppheadnewnode;return;}// 寻找 pos 的前驱节点Node*cur*pphead;while(cur-next!pos)curcur-next;cur-nextnewnode;newnode-nextpos;}说明若 pos 是头节点会改变链表头部使用二级指针。5.5. 指定节点后插入 SLTInsertAftervoidSLTInsertAfter(Node*pos,LinkData x){Node*newnodeBuyNode(x);newnode-nextpos-next;pos-nextnewnode;}说明永远不会改动链表头部仅修改节点内部 next一级指针够用。5.6. 销毁链表 SLTDestroyvoidSLTDestroy(Node**pphead){assert(pphead);Node*cur*pphead;while(cur){Node*nextcur-next;free(cur);curnext;}// 外部头指针置空防止野指针*ppheadNULL;}说明销毁后需要清空外部头指针变量必须二级指针。总结需要修改外部头指针头删、尾删、pos 前插、销毁链表 → 参数 Node**仅遍历 / 后置插入不改动表头查找、pos 后插 → 参数 Node*二级指针核心逻辑C 语言值传递只有传入指针变量的地址才能修改外部原始指针。

相关新闻

2026/8/29 19:02:40

静态NAT、动态NAT与PAT:从原理到实战配置的深度解析

1. NAT技术基础:从地址枯竭到网络桥梁想象一下你住在一个小区里,每家每户都有内部房间号(比如A栋101),但对外只用一个统一的小区地址。当快递员送货时,物业负责把包裹从小区大门转送到具体住户——这就是NA…

2026/9/4 0:56:25

魔芋 AI:全球大模型一站式调用效果实测

在开发智能应用时,最让人头疼的往往不是算法逻辑本身,而是如何稳定、高效地连接到合适的大模型。很多开发者都经历过这样的场景:本地测试一切正常,一旦上线就面临接口超时、密钥管理混乱、不同模型切换成本高昂等问题。尤其是当业…

2026/9/5 0:24:49

Qwen3.8-Flash-Next实战指南:架构预览与低成本微调

Qwen4架构的预览版放出来那天,我正在本地核对一批长文本评测集。看到“Qwen3.8-Flash-Next开源,训练成本仅为前代1/9”这两句话时,第一反应是这可能又是一次营销式刷分。但真正把开源权重拉下来跑了一遍之后,我发现这次的动作比名…

2026/9/3 18:28:26

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/3 14:29:47

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/3 14:30:35

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/5 0:04:47

流式背压机制:避免前端渲染卡死与内存暴涨的滑动窗口限流

流式背压机制:避免前端渲染卡死与内存暴涨的滑动窗口限流在大模型流式输出(Streaming)与智能体实时推流的架构中,生产环境中经常出现一种“上下游生产消费速率严重失衡”的极端情况: 生产端极速产出:大模型…

2026/9/3 20:43:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/3 17:51:43

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/3 21:06:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…