《数据结构笔记三》:单链表(创建、插入、遍历、删除、释放内存等核心操作)
不带头节点的单链表:(不带表头)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//定义一个链表节点结构体
typedef struct Node
{/* data */int data; //表示节点数据域struct Node *next; //表示节点指针域
}Node;//链表类型 (头指针)
typedef Node *LinkedList;void initList(LinkedList *list)
{*list = NULL;
}//插入新节点 头插法
void insertAtHead(LinkedList *list int value)
{Node *newnode = (Node*) malloc (sizeof(Node)); //为新节点开辟堆内存空间if(!newnode)//判断是否存在新节点{perror("内存分配失败!");exit(EXIT_FAILURE);}newNode -> data = value;newNode -> next = *list; //新节点指向原头节点*list = Newnode; //头指针指向新节点
}//遍历链表
void traverseList(LinkedList list)
{Node *current = list;printf("链表元素:");while (current != NULL){/* code */printf("%d ->",current -> data);current = currenr -> next;}printf("NULL\n");
}//删除节点
//删除第一个值为vulue的节点
void deleteNode(LinkedList *list , int value)
{if ( *list= NULL) //判断是否为空链表{/* code */return ;}Node *current = *list;Node *prev = NULL;//查找待删除节点while (current != NULL && current->data != value){/* code */prev = current;current = current->next;}if(current = NULL) return; //空值表示未查找到if (prev = NULL){*list = current->next;}else{prev->next = current->next;}free(current); //释放节点内存
}//释放内存
void freeList(Linkedlist *list)
{Node *current = *list;while (current != NULL){Node *temp = current;current = current->next;free(temp); }*list = NULL; // 头指针置空
}int main() {// 测试不带表头链表LinkedList list1;initList(&list1);insertAtHead(&list1, 10);insertAtHead(&list1, 20);traverseList(list1); // 输出: 20 -> 10 -> NULLdeleteNode(&list1, 10);traverseList(list1); // 输出: 20 -> NULLfreeList(&list1);return 0;
}
带头节点的单链表:(带表头)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>//定义一个链表节点结构体
typedef struct Node {int data;struct Node *next;
} Node;// 链表类型(固定头节点)
typedef struct {Node *head; // 头节点(不存储数据 NULL)
} LinkedList;//创建链表,初始化
void initList(LinkedList *list) {list->head = (Node *)malloc(sizeof(Node)); // 分配头节点if (!list->head) {perror("内存分配失败");exit(EXIT_FAILURE);}list->head->next = NULL; // 头节点的next初始化为NULL
}//插入节点(头插法)
void insertAtHead(LinkedList *list, int value) {Node *newNode = (Node *)malloc(sizeof(Node));if (!newNode) {perror("内存分配失败");exit(EXIT_FAILURE);}newNode->data = value;newNode->next = list->head->next; // 新节点指向原第一个节点list->head->next = newNode; // 头节点指向新节点
}//遍历节点
void traverseList(LinkedList *list) {Node *current = list->head->next; // 跳过头节点printf("链表元素: ");while (current != NULL) {printf("%d -> ", current->data);current = current->next;}printf("NULL\n");
}//删除节点
void deleteNode(LinkedList *list, int value) {Node *prev = list->head;Node *current = prev->next;while (current != NULL && current->data != value) {prev = current;current = current->next;}if (current != NULL) {prev->next = current->next;free(current);}
}//释放内存
void freeList(LinkedList *list) {Node *current = list->head->next;while (current != NULL) {Node *temp = current;current = current->next;free(temp);}free(list->head); // 释放头节点list->head = NULL;
}int main()
{// 测试带表头链表LinkedList list2;initList(&list2);insertAtHead(&list2, 100);insertAtHead(&list2, 200);traverseList(&list2); // 输出: 200 -> 100 -> NULLdeleteNode(&list2, 200);traverseList(&list2); // 输出: 100 -> NULLfreeList(&list2);return 0;}
插入操作,除了头插法,还有:中间插入、任意一位置插入(索引插入),尾插法。
1. 插入操作的分类
插入方式 | 适用场景 | 时间复杂度 |
---|---|---|
头插法 | 快速在链表头部插入 | O(1) |
尾插法 | 在链表尾部追加数据 | O(n) |
中间插入 | 在指定值或位置后插入 | O(n)(查找时间) |
按索引插入 | 类似数组操作,按位置插入 | O(n) |
2. 带表头 vs 不带表头的差异
操作 | 不带表头链表 | 带表头链表 |
---|---|---|
头插法 | 需直接修改外部头指针 | 操作头节点的 next ,无需修改外部指针 |
尾插法 | 需要处理空链表(头指针为NULL) | 直接从 head->next 开始遍历 |
按索引插入 | 插入位置0需特殊处理头指针 | 统一从头节点开始计算位置 |
错误处理 | 需检查头指针是否为NULL | 头节点始终存在,无需额外判空 |
3. 边界条件处理
-
空链表插入:
-
不带表头:头指针为NULL时,尾插法直接赋值。
-
带表头:头节点的
next
为NULL时,尾插法插入到head->next
。
-
-
越界插入:
-
按索引插入时,若
pos
超过链表长度,需提示错误并释放未使用的节点内存。
-
-
重复值处理:
-
中间插入时,若有多个相同值的节点,默认操作第一个匹配的节点。
-
4. 内存管理
-
内存分配失败:统一检查
malloc
返回值,避免程序崩溃。 -
未使用的节点:在插入失败时(如越界),需释放已分配的节点内存。
总结
-
插入灵活性:单链表的插入操作灵活,但需注意指针修改顺序(先连接新节点,再断开旧链接)。
-
代码鲁棒性:始终处理边界条件(如空链表、越界位置)和内存分配失败。
-
性能权衡:头插法最快(O(1)),尾插法和中间插入需要遍历(O(n))。
深度解析
1. 带表头 vs 不带表头
特性 | 不带表头链表 | 带表头链表 |
---|---|---|
头指针 | 指向第一个数据节点 | 指向头节点(不存储数据) |
插入/删除头节点 | 需要特殊处理头指针 | 统一操作,无需修改头指针 |
代码简洁性 | 需要较多条件判断 | 代码更统一,减少边界条件 |
内存占用 | 节省一个头节点内存 | 多一个头节点的内存占用 |
适用场景 | 简单场景或内存敏感环境 | 需要频繁操作头节点的情况 |
2. 关键操作分析
-
插入操作:
-
不带表头链表插入头节点时,必须修改外部头指针(需传递二级指针)。
-
带表头链表插入头节点时,只需修改头节点的
next
指针,外部头指针不变。
-
-
删除操作:
-
不带表头链表删除头节点时,需更新头指针,否则会访问已释放的内存。
-
带表头链表删除头节点时,只需处理头节点的
next
指针,无需关心外部指针。
-
-
内存管理:
-
释放链表时,带表头链表需额外释放头节点。
-
不带表头链表需确保头指针被正确置空,避免野指针。
总结
-
不带表头链表:适合简单场景,但需处理头指针的特殊情况。
-
带表头链表:代码更简洁,适合需要频繁操作头节点的场景。
-
通过对比实现,可以深入理解链表的核心逻辑和指针操作的精髓。
-
共同注意点:确保内存正确释放,避免内存泄漏;操作指针时注意边界条件。
-
相关文章:
《数据结构笔记三》:单链表(创建、插入、遍历、删除、释放内存等核心操作)
不带头节点的单链表:(不带表头) #include<stdio.h> #include<stdlib.h> #include<string.h> //定义一个链表节点结构体 typedef struct Node {/* data */int data; //表示节点数据域struct Node *next; //…...
光伏行业如何利用SD-WAN优化分布式网络:替代MPLS、VPN、4G/5G的网络架构升级与云安全方案全解析
光伏行业的网络通信挑战与SD-WAN技术方案对比分析 光伏行业的分布式网络架构、远程站点多、通信环境复杂,以及对实时数据传输的高要求,使得网络架构成为影响行业数字化转型的重要因素。近年来,SD-WAN(软件定义广域网)…...

2025电工杯数学建模A题思路数模AI提示词工程
我发布的智能体链接:数模AI扣子是新一代 AI 大模型智能体开发平台。整合了插件、长短期记忆、工作流、卡片等丰富能力,扣子能帮你低门槛、快速搭建个性化或具备商业价值的智能体,并发布到豆包、飞书等各个平台。https://www.coze.cn/search/n…...

LLM | 论文精读 | NAACL 2025 | Clarify When Necessary:教语言模型何时该“问一句”再答!
🔍 解读 NAACL 2025 重磅论文《Clarify When Necessary》:教语言模型何时该“问一句”再答! 🧩 一、现实问题:大模型“看不懂装懂”有多危险? 我们每天用的 ChatGPT、Claude 等大型语言模型(LL…...

嵌入式鸿蒙openharmony应用开发环境搭建与工程创建实现
各位小伙伴大家好,本周开始分享鸿蒙开发相关的内容,从基础的配置方法到各种功能的实现,探索国产操作系统的奥秘。 第一:观察结果 第二:开源语言 ArkTS是鸿蒙应用开发中使用的TypeScript超集,提供了一套丰富的API来构建应用界面和逻辑。 第三:环境搭建 步骤 1 通过如…...

MDK的编译过程及文件类型全解
本章参考资料:MDK的帮助手册《ARM Development Tools》,点击MDK界面的“help->uVision Help”菜单可打开该文件。 关于ELF文件格式,参考配套资料里的《ELF文件格式》文件。 在本章中讲解了非常多的文件类型,学习时请跟着教程的…...
socc 19 echash论文部分解读
前言:论文还是得吃透才行,不然很多细节有问题 q1 object和data chunck哪一个大 根据论文,一个 data chunk 通常比一个 object 大,因为它是由多个 object 组合而成的 。 论文中提到,cross-coding 会将多个 object 组合…...

Linux Shell编程(八)
目录 Case语句 1--case格式 2--case使用案例:输入不容的数字,给出不同的结果 跳出循环 1--break 案例:执行十次时,跳出当前循环 完整流程 2--continue 案例:跳过2,4 输出 完整流程 Case语句 1--case格式 c…...

AI筑基,新质跃升|英码科技亮相华为广东新质生产力创新峰会,发布大模型一体机新品,助力产业智能化转型
5月15日,以“AI筑基,新质跃升”为主题的华为中国行2025广东新质生产力创新峰会在惠州圆满召开。本次峰会聚焦人工智能、算力基础设施等新ICT技术如何驱动“新质生产力”,共探广东高质量发展新路径。英码科技受邀出席本次峰会,并携…...

手机打电话时由对方DTMF响应切换多级IVR语音菜单(话术脚本与实战)
手机打电话时由对方DTMF响应切换多级IVR语音菜单 (话术脚本与实战) --本地AI电话机器人 上一篇:手机打电话时由对方DTMF响应切换多级IVR语音应答(二) 下一篇:手机打电话时由对方DTMF响应切换多级IVR语音…...

面试题——JDBC|Maven|Spring的IOC思想|DI思想|SpringMVC
目录 一、JDBC 1、jdbc连接数据库的基本步骤(掌握**) 2、Statement和PreparedStatement的区别 (掌握***) 二、Maven 1、maven的作用 2、maven 如何排除依赖 3、maven scope作用域有哪些? 三、Spring的IOC思想 …...

DETR3D- 3D Object Detection from Multi-view Images via 3D-to-2D Queries
MIT CORL 2021 纯视觉BEV方案transformer网络3D检测 paper:[2110.06922] DETR3D: 3D Object Detection from Multi-view Images via 3D-to-2D Queries code:GitHub - WangYueFt/detr3d DNN提图像特征,FPN提多尺度特征 pts_bbox_head Detr3…...

SpringBoot3整合WebSocket
一、WebSocket简介 WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信,允许服务器主动向客户端推送数据。 与传统的 HTTP 请求-响应模式不同,WebSocket 在建立连接后,允许服务器和客户端之间进行双向…...

鸿蒙进阶——驱动框架UHDF 机制核心源码解读(一)
文章大纲 引言一、uhdf 概述二、uhdf 的核心参与角色1、drivers/hdf_core/adapter/uhdf2/manager/device_manager.c1.1、drivers/hdf_core/framework/core/manager/src/devmgr_service.c#DevmgrServiceGetInstance通过objectId获取IDevmgrService实例1.2、drivers/hdf_core/fra…...
电子电路:能认为电抗也是在做功吗?
阻抗是什么,我记得在交流电路中,阻抗是电阻、电感和电容的综合作用,用Z表示,单位是欧姆。 那阻抗和做功的关系,可能需要从阻抗的组成来分析。阻抗分为电阻部分和电抗部分,也就是 Z = R + jX,其中R是电阻,X是电抗(包括感抗和容抗)。而做功可能主要和电阻有关,因为电…...
DEEPSEEK + 其他工具的玩法
1. deepseek 即梦,批量生成图片 1)给deepseek提出需求,让他生成一个海报设计框架 2)让deepseek把上面的框架转换为文生图的提示词,方便用来制作图片 3)将提示词复制到 即梦(即梦电脑…...

Idea 配合 devtools 依赖 实现热部署
核心依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency> yaml配置 spring: #…...
远程访问家里的路由器:异地访问内网设备或指定端口网址
在一些情况下,我们可能需要远程访问家里的路由器,以便进行设置调整或查看网络状态等,我们看看怎么操作? 1.开启远程访问 在路由本地电脑或手机,登录浏览器访问路由管理后台,并设置开启WEB远程访问。 2.内…...
根据参数量,如何推断需要多少数据才能够使模型得到充分训练?
✅ 一、经验法则:数据量 vs. 模型参数量 经典经验法则(适用于监督学习场景): 训练样本数 ≈ 模型参数数量的 10~100 倍对于 BERT-base(1.1亿参数),你通常需要 10亿到100亿标注样本 才能从头训…...
PycharmFlask 学习心得:路由(3-4)
对路由的理解: 用户输入网址 例如:http://localhost:5000/hello 浏览器会向这个地址发起一个 HTTP 请求(比如 GET 请求) 请求到达 Flask 的服务器 Flask 监听着某个端口(如 5000),收到请求后…...

从逻辑学视角严谨证明数据加密的数学方法与实践
文章目录 一、加密数据的数学指纹:信息论基础1.1 加密检测的核心原理1.2 香农熵:量化信息的不确定性 二、统计检验方法:从随机性到加密性2.1 卡方检验的数学原理2.2 游程检验与序列相关性2.3 NIST统计测试套件 三、加密算法的特征识别3.1 对称…...

敦煌网测评从环境搭建到风控应对,精细化运营打造安全测评体系
自养号测评,抢占流量为快速提升产品权重和销量,很多卖家常采用自己养号补单测评的方式,技术搭建需要很多要素 一、硬件参数的关联性 在我们使用设备进行注册或操作账号的过程中,系统会记录下大量的系统与网络参数,其中…...
现代化SQLite的构建之旅——解析开源项目Limbo
现代化SQLite的构建之旅——解析开源项目Limbo 在当今飞速发展的技术世界中,轻量级且功能强大的数据库已成为开发者的得力助手。当我们谈论轻量级数据库时,SQLite无疑是一个举足轻重的名字。然而,随着技术的进步,我们对数据库的需求也变得更加多样化。这正是Limbo项目诞生…...

本地分支git push 报错 fatal: The current branch XXXX has no upstream branch.
背景: 我新建了一个本地分支叫做 “新增Saas修改需求”,然后当我提交代码执行 git push时报错如下,并且代码仓库中没有我新建的“新增Saas修改需求”这个分支。 报错信息: 解决方法: 直接采用方法2 ”git push -u orig…...
人工智能100问☞第27问:神经网络与贝叶斯网络的关系?
神经网络与贝叶斯网络是两种互补的智能模型:神经网络通过多层非线性变换从数据中学习复杂模式,擅长大规模特征提取和预测,而贝叶斯网络基于概率推理建模变量间的条件依赖关系,擅长处理不确定性和因果推断。两者的融合(如贝叶斯神经网络)结合了深度学习的表征能力与概率建…...

Python----循环神经网络(WordEmbedding词嵌入)
一、编码 当我们用数字来让电脑“认识”字符或单词时,最简单的方法是为每个字符或单词分配一个唯一的编号,然后用一个长长的向量来表示它。比如,假设“我”这个字在字典中的编号是第10个,那么它的表示就是一个很多0组成的向量&…...
ElasticSearch各种查询语法示例
1. 每种查询语法的区别与优缺点 Query DSL 区别: JSON 格式的结构化查询,功能强大,支持复杂查询逻辑,适用于 Elasticsearch 的核心查询场景。优点: 灵活,功能全面,支持全文搜索、精确匹配、聚合等;可组合…...

CUDA的设备,流处理器(Streams),核,线程块(threadblock),线程,网格(gridDim),块(block)和多gpu设备同步数据概念
CUDA的设备,流处理器,核,线程块(threadblock),线程,网格(gridDim),块(block)和多gpu设备同步数据概念 CUDA的设备,流处理器,核&…...
PyTorch的dataloader制作自定义数据集
PyTorch的dataloader是用于读取训练数据的工具,它可以自动将数据分割成小batch,并在训练过程中进行数据预处理。以下是制作PyTorch的dataloader的简单步骤: 导入必要的库 import torch from torch.utils.data import DataLoader, Dataset定…...

LeetCode 1340. 跳跃游戏 V(困难)
题目描述 给你一个整数数组 arr 和一个整数 d 。每一步你可以从下标 i 跳到: i x ,其中 i x < arr.length 且 0 < x < d 。i - x ,其中 i - x > 0 且 0 < x < d 。 除此以外,你从下标 i 跳到下标 j 需要满…...