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

C语言链表完全指南:从单节点到链表管理

引言在数据结构的学习中我们首先学习了顺序表数组。顺序表虽然访问速度快但插入和删除操作需要移动大量元素效率较低。此外顺序表的大小固定扩容需要重新分配内存并拷贝数据。链表解决了这些问题。链表中的元素在内存中不连续存储通过指针连接形成逻辑上的线性结构。今天我们将从零开始实现一个完整的带头结点的单向链表涵盖初始化、插入、删除、遍历等核心操作。第一部分链表的基本概念一、什么是链表链表是一种物理存储单元上非连续、非顺序的线性结构。数据元素之间的逻辑关系通过指针链接实现。链表的分类分类标准类型说明方向单向链表每个节点只有一个指向后继的指针双向链表每个节点有前驱和后继两个指针是否循环非循环链表尾节点指针指向NULL循环链表尾节点指针指向头节点是否有头结点带头结点头结点不存储数据简化操作不带头结点第一个节点就存储数据本文实现的是带头结点的单向非循环链表。二、带头结点的优势特点不带头结点带头结点空表判断head NULLhead-next NULL头插操作需要修改头指针操作统一不修改头指针删除头节点需要修改头指针操作统一代码复杂度较高较低第二部分数据结构设计一、节点结构体typedef int ElemType; // 链表存储的数据类型 // 节点结构体 typedef struct ListNode { ElemType data; // 数据域存储实际数据 struct ListNode* next; // 指针域指向下一个节点 } ListNode;节点结构体大小┌─────────────────────────────────────────────┐ │ ListNode 节点 │ ├─────────────────────────────────────────────┤ │ data (4字节) │ next (8字节64位系统) │ └─────────────────────────────────────────────┘ 总大小12字节可能内存对齐后为16字节二、链表管理结构体// 链表管理结构体 struct LinkList { ListNode* head; // 指向头结点不存储数据 size_t cursize; // 当前链表中的元素个数 };设计意图head始终指向头结点头结点的next指向第一个实际存储数据的节点cursize记录链表长度避免每次遍历计算第三部分链表的基本操作一、初始化原理创建头结点让head指向它头结点的next指向NULL。void InitLinkList(struct LinkList* ps) { assert(ps ! NULL); // 分配头结点内存 ListNode* p (ListNode*)malloc(sizeof(ListNode)); if (p NULL) return; p-data 0; // 头结点数据域无用可任意赋值 p-next NULL; // 初始时没有实际节点 ps-head p; ps-cursize 0; }初始化后的内存布局二、创建新节点辅助函数ListNode* BuyNode(ElemType val) { ListNode* p (ListNode*)malloc(sizeof(ListNode)); if (p NULL) return p; p-data val; p-next NULL; return p; }三、遍历与打印void PrintLinkList(struct LinkList* ps) { assert(ps ! NULL); // 从第一个实际节点开始遍历跳过带头结点 for (ListNode* p ps-head-next; p ! NULL; p p-next) { printf(%d , p-data); } printf(\n); }第四部分插入操作一、尾插在尾部插入原理遍历到尾节点将新节点链接到尾部。// 时间复杂度O(n) bool InsertBack(struct LinkList* ps, ElemType val) { assert(ps ! NULL); ListNode* p BuyNode(val); if (p NULL) return false; // 找到尾节点 ListNode* q ps-head; while (q-next ! NULL) { q q-next; } // 链接新节点 p-next q-next; // 等价于 p-next NULL q-next p; ps-cursize; return true; }示意图插入前 : head → ┌───┐ ┌───┐ ┌───┐ │10 │→ │20 │→ │30 │→ NULL └───┘ └───┘ └───┘ 插入val40后 ┌───┐ ┌───┐ ┌───┐ ┌───┐ │10 │→ │20 │→ │30 │→ │40 │→ NULL └───┘ └───┘ └───┘ └───┘二、头插在头部插入原理新节点的next指向原来的第一个节点头结点指向新节点。// 时间复杂度O(1) bool InsertFront(struct LinkList* ps, ElemType val) { assert(ps ! NULL); ListNode* p BuyNode(val); if (p NULL) return false; p-next ps-head-next; // 新节点指向原第一个节点 ps-head-next p; // 头结点指向新节点 ps-cursize; return true; }示意图三、按位置插入原理找到第pos-1个节点在其后插入新节点。// 时间复杂度O(n) bool InsertPos(struct LinkList* ps, ElemType val, int pos) { assert(ps ! NULL); // 位置有效性检查1 ≤ pos ≤ cursize1 if (pos 1 || pos ps-cursize 1) return false; ListNode* p BuyNode(val); if (p NULL) return false; // 找到第 pos-1 个节点 ListNode* q ps-head; for (int i 0; i pos - 1; i) { q q-next; } // 插入新节点 p-next q-next; q-next p; ps-cursize; return true; }第五部分删除操作一、尾删删除尾部节点原理找到倒数第二个节点将其next设为NULL释放尾节点。// 时间复杂度O(n) bool PopBack(struct LinkList* ps) { assert(ps ! NULL); if (ps-cursize 0) return false; // 空链表 // 找到倒数第二个节点 ListNode* q ps-head; while (q-next-next ! NULL) { q q-next; } // 释放尾节点 ListNode* p q-next; q-next p-next; // 即 q-next NULL free(p); p NULL; ps-cursize--; return true; }二、头删删除头部节点原理头结点绕过第一个节点指向第二个节点释放第一个节点。// 时间复杂度O(1) bool PopFront(struct LinkList* ps) { assert(ps ! NULL); if (ps-cursize 0) return false; ListNode* p ps-head-next; // 要删除的节点 ps-head-next p-next; // 头结点指向第二个节点 free(p); p NULL; ps-cursize--; return true; }三、按位置删除// 时间复杂度O(n) bool PopPos(struct LinkList* ps, int pos) { assert(ps ! NULL); if (ps-cursize 0) return false; if (pos 1 || pos ps-cursize) return false; // 找到第 pos-1 个节点 ListNode* q ps-head; for (int i 0; i pos - 1; i) { q q-next; } // 删除第 pos 个节点 ListNode* p q-next; q-next p-next; free(p); p NULL; ps-cursize--; return true; }第六部分完整测试代码#include List.h #include stdio.h int main() { struct LinkList list; // 1. 初始化 InitLinkList(list); printf(初始化后元素个数%zu\n, list.cursize); // 2. 头插 InsertFront(list, 10); InsertFront(list, 20); InsertFront(list, 30); printf(头插后); PrintLinkList(list); // 30 20 10 // 3. 尾插 InsertBack(list, 40); InsertBack(list, 50); printf(尾插后); PrintLinkList(list); // 30 20 10 40 50 // 4. 按位置插入 InsertPos(list, 100, 3); // 在第3个位置插入100 printf(位置插入后); PrintLinkList(list); // 30 20 100 10 40 50 // 5. 头删 PopFront(list); printf(头删后); PrintLinkList(list); // 20 100 10 40 50 // 6. 尾删 PopBack(list); printf(尾删后); PrintLinkList(list); // 20 100 10 40 // 7. 按位置删除 PopPos(list, 2); printf(删除位置2后); PrintLinkList(list); // 20 10 40 return 0; }第七部分顺序表 vs 链表操作顺序表链表说明按下标访问O(1)O(n)链表需要遍历头部插入O(n)O(1)链表只需改指针头部删除O(n)O(1)链表只需改指针尾部插入O(1)扩容时O(n)O(n)链表需遍历到尾中间插入O(n)O(n)都需要定位内存占用较少较多链表有指针开销缓存友好好差顺序表内存连续选择建议频繁按下标访问 → 顺序表频繁头插/头删 → 链表频繁尾插 → 顺序表或维护尾指针的链表大小频繁变化 → 链表第八部分常见错误与注意事项一、指针操作错误// ❌ 错误p未初始化就修改next ListNode* p; p-next NULL; // ✅ 正确先分配内存 ListNode* p (ListNode*)malloc(sizeof(ListNode)); p-next NULL;二、内存泄漏// ❌ 错误删除节点后未释放内存 q-next p-next; // p 的内存泄漏了 // ✅ 正确释放内存 q-next p-next; free(p); p NULL;三、空指针解引用// ❌ 错误未检查链表是否为空 bool PopFront(struct LinkList* ps) { ListNode* p ps-head-next; ps-head-next p-next; // 如果p是NULL这里崩溃 free(p); return true; } // ✅ 正确检查空链表 bool PopFront(struct LinkList* ps) { if (ps-cursize 0) return false; // ... 正常删除逻辑 }总结一、链表操作复杂度总结操作时间复杂度代码关键点初始化O(1)创建头结点尾插O(n)遍历到尾节点头插O(1)新节点指向原第一个节点中间插入O(n)定位到前驱节点尾删O(n)找到倒数第二个节点头删O(1)头结点指向第二个节点中间删除O(n)定位到前驱节点遍历O(n)从头结点下一个开始二、带头结点的优势场景不带头结点带头结点空表判断head NULLhead-next NULL头插操作需要修改头指针操作统一删除头节点需要修改头指针操作统一三、核心函数速查函数功能关键操作InitLinkList初始化创建头结点BuyNode创建节点malloc 赋值InsertBack尾插遍历到尾 链接InsertFront头插头结点后插入InsertPos位置插入找前驱 链接PopBack尾删找倒数第二个 释放PopFront头删头结点绕过第一个PopPos位置删除找前驱 释放链表是数据结构中非常重要的基础结构。本文实现了带头结点的单向链表涵盖以下知识点节点结构包含数据域和指针域头结点简化插入删除操作插入操作头插、尾插、按位置插入删除操作头删、尾删、按位置删除内存管理malloc分配、free释放复杂度分析理解各操作的时间复杂度学习建议画图理解指针操作不要只靠想象注意边界条件空链表、单节点链表插入和删除时确保指针指向正确释放内存后将指针置NULL防止野指针

相关文章:

C语言链表完全指南:从单节点到链表管理

引言在数据结构的学习中,我们首先学习了顺序表(数组)。顺序表虽然访问速度快,但插入和删除操作需要移动大量元素,效率较低。此外,顺序表的大小固定,扩容需要重新分配内存并拷贝数据。链表解决了…...

本地代码智能引擎CIE:基于MCP协议为AI助手注入语义理解能力

1. 项目概述:为AI智能体注入“代码理解力”的本地引擎如果你和我一样,每天都要在动辄数万、甚至数十万行代码的仓库里穿梭,只为找到一个特定功能的实现,或者理清某个函数错综复杂的调用链路,那你一定明白那种“大海捞针…...

深入解析libclang的多维数组处理

深入解析libclang的多维数组处理 在C++编程中,多维数组是一个常见的概念。然而,通过libclang来解析这些数组的维度信息却并非那么直观。本文将详细介绍如何使用Python的libclang绑定来解析C++结构体中的多维数组,并展示如何获取每个维度的具体大小。 C++中的多维数组 首先…...

基于双Transformer的网球轨迹预测系统设计与实现

1. 轨迹预测技术概述轨迹预测作为计算机视觉与运动分析领域的核心技术,在航空航天、智能交通和体育竞技等多个领域具有广泛应用价值。传统方法主要依赖复杂的物理建模或大量标注数据,不仅计算效率低下,还面临硬件成本高昂的挑战。以网球运动为…...

构建AI提示词锻造炉:从碎片化到工程化的高效管理实践

1. 项目概述:为什么我们需要一个AI提示词锻造炉?如果你和我一样,每天都在和ChatGPT、Cursor、Claude这些AI助手打交道,那你一定经历过这样的时刻:面对一个复杂任务,你明明记得上周写过一个绝佳的提示词&…...

从Figma设计稿自动生成CSS代码:design-extract工具实战指南

1. 项目概述:从设计稿到代码的自动化提取最近在跟一个前端团队合作,他们每天都要面对一个老大难问题:UI设计师用Figma或Sketch交付了精美的设计稿,前端同学就得拿着像素尺,一个个去量间距、颜色、字体大小,…...

华恒智信助力高速成长型科技行业完成敏捷任职资格体系重塑

在业务以月为单位高速迭代的当下,许多科技型企业的管理者正面临一个日益尖锐的矛盾:前端市场拼命抢时间,产品线两周一个版本,研发团队加班加点赶上线,但核心人才的晋升评估却依然被锁定在“年底考核、论资排辈”的传统…...

SketchUp模型高效导出CAD施工图:平面、立面、剖面及效果图的DWG导出全解析

SketchUp模型高效导出CAD施工图:平面、立面、剖面及效果图的DWG导出全解析 在建筑与室内设计领域,SketchUp和CAD的协同工作已成为行业标配。许多设计师习惯用SketchUp进行快速建模和方案推敲,但最终施工图仍需在CAD中完成。如何将SketchUp中的…...

娱乐圈天降紫微星自带气运,海棠山铁哥无背景照样登顶巅峰

真正的强者,命里自带光芒,不需要外界加持。01 行业迷信 娱乐圈向来迷信 资源、背景、人脉、资本。 没有靠山 ≈ 没有姓名。02 假紫微星 背靠大平台、手握老牌 IP、团队全程包装。 作品流水线代工,离开资本托举立刻露馅。 声势浩大&#xff0c…...

React声明式数据表格方案:基于Schema与适配器的企业级实践

1. 项目概述:一个为现代React应用而生的声明式数据表格方案 如果你正在用React构建一个需要复杂数据展示和交互的后台管理系统、监控面板或者数据分析工具,那么“如何优雅地实现一个功能强大的数据表格”这个问题,大概率已经让你头疼过不止一…...

家装壁炉选型避坑指南:真火、电壁炉、雾化壁炉怎么选?纽波特铸铁壁炉实测分享

在家庭装修中,壁炉不仅是提升居家氛围感的重要软装,更是部分家庭冬季取暖的补充选择。但很多业主在选型时都会陷入迷茫,分不清真火壁炉、电壁炉、雾化壁炉的差异,也不知道嵌入式、独立式、挂壁式哪款更适配自家户型,盲…...

构建生产级AI智能体的六层设计模式与工程实践

1. 项目概述:从“模型循环”到“生产级智能体”的鸿沟如果你最近在捣鼓AI智能体,尤其是那些能写代码的AI助手,你肯定对User -> LLM -> tool_use -> execute -> loop这个循环不陌生。这个模型循环简单到可以画在餐巾纸上&#xff…...

告别标准库:用STM32CubeMX+HAL库玩转蓝桥杯CT117E开发板的5个实战项目

告别标准库:用STM32CubeMXHAL库玩转蓝桥杯CT117E开发板的5个实战项目 在嵌入式开发领域,从标准库转向HAL库已成为不可逆转的趋势。对于参加蓝桥杯嵌入式赛项的选手来说,掌握HAL库开发不仅能够提升开发效率,更能适应现代嵌入式开发…...

保姆级教程:用CloudCompare一键搞定点云最小包围盒(附PCA原理白话解读)

从零掌握点云最小包围盒:CloudCompare实战与PCA原理拆解 第一次接触点云处理时,看着屏幕上密密麻麻的三维坐标点,最让我头疼的就是如何快速确定这些散乱数据的空间范围。传统AABB包围盒就像用标准纸箱装不规则物品,总有多余空间浪…...

区域知识产权信息管理:创新监管,智慧服务

为赋能区域知识产权管理,助力区域科技创新和经济发展,“普陀区知识产权信息服务平台”上线运行。平台整合“区域实时监控统计”“知识产权信息统计”“园区知识产权代管”“企业排行榜”“专利检索”“商标检索”六大核心功能模块,覆盖政务决…...

Go开发者必备:andrewstuart/openai库实战指南与最佳实践

1. 项目概述:一个为Go开发者打造的OpenAI API封装库如果你是一名Go开发者,正在寻找一个能让你快速、优雅地接入OpenAI强大AI能力(比如ChatGPT、DALLE、Whisper)的工具,那么andrewstuart/openai这个项目很可能就是你一直…...

利用快马平台快速构建Hermes Agent多模态AI演示原型

最近在研究多模态AI智能体框架时,发现了开源的Hermes Agent项目。它最吸引我的地方是能够处理图片、文档等不同模态的输入,并给出智能响应。为了快速验证它的能力,我尝试在InsCode(快马)平台上搭建了一个演示原型,整个过程比想象中…...

Rust语言GPU推理引擎nblm-rs:专为NVIDIA优化的轻量级大模型部署方案

1. 项目概述:一个为NVIDIA GPU优化的Rust语言推理引擎最近在折腾大模型本地部署和推理加速,尤其是在资源受限的边缘设备上,总感觉现有的框架要么太重,要么对特定硬件的优化不够极致。直到我遇到了nblm-rs这个项目,它让…...

2026指纹浏览器常见故障排查与运维实战手册

在指纹浏览器规模化应用的 2026 年,无论是企业级多账号运营,还是个人隐私防护,工具的稳定运行都是核心前提。但在实际使用过程中,受设备配置、网络环境、参数设置、平台风控迭代等多种因素影响,指纹浏览器难免出现各类…...

零基础入门爬虫:借助快马AI理解OpenClaw101框架的核心使用步骤

作为一个刚接触爬虫的小白,最近在InsCode(快马)平台上尝试用OpenClaw101框架做了些练习,发现这个工具对新手特别友好。今天就把我的学习过程整理成笔记,分享给同样想入门爬虫的朋友们。 环境准备与基础认知 刚开始完全不懂什么是爬虫框架&…...

PM Pilot v2.0.0:基于本地知识库的AI产品管理副驾驶实战指南

1. 项目概述:一个为产品经理量身打造的AI副驾驶如果你是一名产品经理,或者正在负责产品决策,那你一定对这样的场景不陌生:面对海量的用户访谈记录,需要手动提炼核心痛点;为了写一份PRD(产品需求…...

Docker 27量子适配终极 checklist:27项硬性校验项(含QPU固件签名验证、量子噪声模型挂载路径、Rust-based Quil compiler容器化兼容性)

更多请点击: https://intelliparadigm.com 第一章:Docker 27量子计算环境适配案例 Docker 27(发布于2024年Q2)首次原生支持Linux内核eBPF加速的量子模拟器调度接口,为Qiskit、Cirq及PennyLane等框架提供了低开销容器化…...

Docker构建镜像实战:打造统一C/C++开发与CI/CD环境

1. 项目概述与核心价值最近在整理个人技术栈和项目资产时,我重新审视了一个名为docker/cc-use-exp的镜像仓库。这个标题乍一看可能有些模糊,但它在容器化开发、持续集成以及多语言环境构建的实践中,扮演着一个相当关键且实用的角色。简单来说…...

AI办公革命:Gemini3.1Pro数据分析实战指南

很多人做数据分析最累的,不是“算”,而是“整理”。 白天开会、回消息、改表格,晚上才有空把零散数据拉出来看一遍:指标很多,不知道先看哪个表格很多,不知道怎么汇总老板问的是“结论”,你却还在…...

Dubbo通信异常(channel is closed)问题分析

一、问题概述 ### 1.1 报错信息 系统运行过程中,消费者服务(support-t1-web)调用Dubbo服务时出现通信异常,具体报错如下: org.apache.dubbo.remoting.RemotingException: message can not send, because channel is…...

安卓手机控制机械爪:软硬件融合开发实践与避坑指南

1. 项目概述:当“机械爪”遇见安卓最近在折腾一个挺有意思的项目,叫Openclaw-on-Android。简单来说,这是一个将开源机械爪(OpenClaw)的控制系统,移植并运行在安卓手机或平板上的工程。你可能在视频网站上见…...

告别VSCode插件!在Ubuntu 20.04上用纯命令行搞定ESP32-CAM摄像头服务器

告别VSCode插件!在Ubuntu 20.04上用纯命令行搞定ESP32-CAM摄像头服务器 当VSCode的ESP-IDF插件突然无法识别你的开发板配置,或者menuconfig界面莫名其妙崩溃时,那种被工具绑架的窒息感会让人怀念起命令行的纯粹。作为经历过三次ESP-IDF大版本…...

MCP 2026租户隔离配置正在失效?——2025年12月补丁强制升级倒计时72小时,附迁移检查清单

更多请点击: https://intelliparadigm.com 第一章:MCP 2026租户隔离配置失效事件全景速览 2026年3月18日,多家采用MCP(Multi-Cloud Platform)v2.6.0核心引擎的云服务商集中报告异常:跨租户资源访问控制策略…...

Rust 模块系统与可见性控制实战:构建清晰的代码结构

Rust 模块系统与可见性控制实战:构建清晰的代码结构 模块系统的重要性 在大型项目中,良好的代码组织是非常重要的。Rust的模块系统提供了一种结构化的方式来组织代码,使得代码更加清晰、可维护,并且可以控制代码的可见性。通过合…...

全球金融监管机构警告:私募信贷行业助推AI热潮存在风险

金融稳定委员会(FSB)发出警告,私募信贷行业在推动AI热潮中扮演的角色可能产生反噬效应,一旦市场出现大幅回调,将导致"相当规模"的损失。这份由全球金融监管机构发布的私募信贷专项报告显示,该机构…...