【数据结构和算法初阶(C语言)】链表-单链表(手撕详讲单链表增删查改)

目录
1.前言:顺序表回顾:
1.1顺序表的优缺点
2.主角----链表
2.1链表的概念
2.2定义一个单链表的具体实现代码方式
3.单链表对数据的管理----增删查改
3.1单链表的创建
3.2单链表的遍历实现
3.2.1利用遍历实现一个打印我们链表内容的函数的函数
3.3头插法----从头部插入数据实现
3.4尾插法---从尾部插入数据
3.5尾删法-----从尾部删除数据
3.6头删法
3.7寻找函数
3.8修改函数
3.9在pos位置前插入
3.10在pos位置后插入
3.11在pos位置前删除
3.12在pos位置后一个位置删除
3.13在pos位置删除
4.补充链表类型暂时了解
5.结语
1.前言:顺序表回顾:
顺序表知识回顾
1.1顺序表的优缺点
- 优点:
1.尾插、尾删速度足够快
2.可以使用下标来进行随机访问和数据修改
- 缺点:
1. 中间/头部的插入删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。比如我们realloc增添空间还会涉及到原始空间数据的拷贝和原始空间的释放。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到 200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
为了解决单链表的这缺点,于是我们引入了链表,链表优点(可以按需申请空间)
2.主角----链表
(今天重要讲解单链表),链表具有八种结构
2.1链表的概念
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。就像火车:

2.2单链表的结构演示
在数据结构中,我们往往会以创建结构体的方式来实现链表,在结构体中定义至少定义两个成员:一个保存我们当前节点的数据,我们称为数据域,另外一个成员保存下一个节点的地址,我们称为指针域。

从上图,我们就可以发现,链式结构在逻辑上是连续的就是我们可以通过指针顺序找到链表中的每个节点但是,在物理上不一定连续,因为我们每一个节点的空间都是通过malloc在堆上申请开辟的空间,那么地址可能不连续。
2.2定义一个单链表的具体实现代码方式
typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;//数据领域struct SListNode* next;//定义指针领域,方便保存下一个节点的地址}SLNode;
为了后期使用方便,我把整型这个类型重新定义了一下,后续如果需要改变我们这个程序中的数据类型,比如换成doubble类型,我们换一下宏定义就好。
![]()
3.单链表对数据的管理----增删查改
3.1单链表的创建
有了结构体类型,那我们就可以创建一个单链表:
1.首先我们先创建一个节点,由于后续插入等等操作都要创建节点,这里我们就可以将节点创建过程分装为一个函数:
SLNode* BuySListNode(SLTDataType x) {//首先为我们的节点申请空间,创建一个节点,就申请这个结构体类型那么大的一个空间就行SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));//判断一下是否开辟成功if (newnode == NULL){perror("malloc");exit(-1);}//开辟成功,就初始化这个节点newnode->data = x;newnode->next = NULL;return newnode;//返回节点地址 }2.然后我们将每个节点链接起来,通个指针和每个节点的地址,这里就涉及头部插入数据和尾部插入数据,我们放在下面实现:
3.2单链表的遍历实现

3.2.1利用遍历实现一个打印我们链表内容的函数的函数
void SLPrint(SLNode* phead) {SLNode* cur = phead;while (cur){printf("%d ->", cur->data);cur = cur->next;//指针移动到下一个节点}printf("NULL");}
3.3头插法----从头部插入数据实现
首先,我们先定义准备一个头指针,用于保存我们链表第一个节点的地址,,方便我们后期使用整块链表。
然后,当我们还没有链表,这时候放入第一个节点:
我们把节点地址给我们的头指针就好。
2.当我们的头指针不为空,这时要插入数据
应该将第一个节点的地址赋值给newnode的next,然后将我们newnode节点作为我们的心的头结点将地址给头指针。
两种情况可以视为一种情况,只是第一种特殊一下,我们的头指针指向空。
注意:在我们写头插函数的时候,传递的是头指针的地址,我们在函数中解引用操作操作的是原先的指针,这样才能整体使用我们的空间,如果我们只是传指针,那么在我们的头插函数中的头指针,只是我们真正头指针的一份拷贝,这样函数结束,形参就销毁,虽然空间开辟了,但是调用结束我们使用的还是原来没插入前的空间。
void SListPushBack(SLNode** phead,int x) {SLNode* newnode = BuySListNode(x);newnode->next = *phead;*phead = newnode;}
3.4尾插法---从尾部插入数据
当我们还没有链表,这时候放入第一个节点:
两种情况实现:
void SLTPushBack(SLNode** phead, SLTDataType x) {SLNode* newnode = BuySListNode(x);//创建新节点if (*phead == NULL){newnode->next = *phead;*phead = newnode;}else{SLNode* cur =*phead;while (cur->next){cur = cur->next;}cur->next = newnode;}}
3.5尾删法-----从尾部删除数据
- ①当我们没有链表此时没有删除的东西,所以不能传入空指针
- ②当我们只有一个节点要删除

那么由于我们头指针有所改变,所以函数传参还是传递我们的头指针的地址
- ③当有很多节点

尽量不用->next->next=NULl的形式做判断,当只有一个节点的时候越界访问。
实现一下:
void SLTPopBack(SLNode** phead) {assert(*phead);//头指针不能为空if ((*phead)->next == NULL){free(*phead);*phead = NULL;}else{SLNode* bfend = NULL;SLNode* end = *phead;while (end->next){bfend = end;end = end->next;}free(bfend->next);bfend->next = NULL;} }
3.6头删法

void SLTPopFront(SLNode** phead) {assert(*phead);//当头指针非空SLNode* newnode =(*phead)->next;(*phead)->next = NULL;free(*phead);*phead = newnode; }
![]()
3.7寻找函数
遍历寻找某个值
SLNode* SLFind(SLNode* phead, SLTDataType x) {SLNode* cur = phead;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;}
3.8修改函数
找到值返回对应位置的指针,然后通过指针修改
void SLMod(SLNode* phead) {int x = 0;printf("请输入你要修改的值:\n");scanf("%d", &x);SLNode* ret = SLFind(phead, x);if (ret){printf("请输入你要修改成什么值\n");int c = 0;scanf("%d", &c);ret->data = c;} }
3.9在pos位置前插入
涉及到头插,就要涉及头指针的改变,那么我们就要传二级指针:
void SLTInsert(SLNode** phead, SLNode* pos, SLTDataType x) {//不能传入空指针assert(pos);if (pos == *phead)//头插{SListPushBack(phead, x);}else{//先遍历到要插入的位置前一个SLNode* pre = *phead;while (pre->next != pos){pre = pre->next;}//然后插入这个新节点,开辟新节点SLNode* newnode = BuySListNode(x);pre->next = newnode;newnode->next = pos;} }
3.10在pos位置后插入

不是不能和头指针相等,是不能为空指针。不可能实现头插
void SLTInsertAfter(SLNode* pos, SLTDataType x) {assert(pos);SLNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode; }
3.11在pos位置前删除
3.12在pos位置后一个位置删除
void SLTEraseAfter(SLNode* pos) {//不能传空指针、头指针、和指向尾节点的指针,没有意义assert(pos);assert(pos->next);SLNode* posNext = pos->next;pos->next = posNext->next;free(posNext);posNext = NULL; }
3.13在pos位置删除
void SLTErase(SLNode** phead, SLNode* pos) {assert(pos);//断言不会传入空指针if (pos == *phead){//头删SLTPopFront(phead);}else{SLNode* pre = *phead;while (pre->next != pos){pre - pre->next;}pre->next = pos->next;free(pos);pos = NULL;} }
4.补充链表类型暂时了解
① 单向或者双向

②带头或者不带头

③循环或者非循环

5.结语
今天的重点在于理解单链表的增删查改,特别是什么时候需要传输二级指针,什么时候不传。以上就是本期的所有内容,知识含量蛮多,大家可以配合解释和原码运行理解。创作不易,大家如果觉得还可以的话,欢迎大家三连,有问题的地方欢迎大家指正,一起交流学习,一起成长,我是Nicn,正在c++方向前行的奋斗者,数据结构内容持续更新中,感谢大家的关注与喜欢。
相关文章:
【数据结构和算法初阶(C语言)】链表-单链表(手撕详讲单链表增删查改)
目录 1.前言:顺序表回顾: 1.1顺序表的优缺点 2.主角----链表 2.1链表的概念 2.2定义一个单链表的具体实现代码方式 3.单链表对数据的管理----增删查改 3.1单链表的创建 3.2单链表的遍历实现 3.2.1利用遍历实现一个打印我们链表内容的函数的函数…...
【Go语言】Go语言中的切片
Go语言中的切片 1.切片的定义 Go语言中,切片是一个新的数据类型数据类型,与数组最大的区别在于,切片的类型中只有数据元素的类型,而没有长度: var slice []string []string{"a", "b", "c…...
Qt程序设计-钟表自定义控件实例
本文讲解Qt钟表自定义控件实例。 效果如下: 创建钟表类 #ifndef TIMEPIECE_H #define TIMEPIECE_H#include <QWidget> #include <QPropertyAnimation> #include <QDebug> #include <QPainter> #include <QtMath>#include <QTimer>#incl…...
Redis的发布订阅功能教程,实现实时消息和key过期事件通知功能
Redis的发布订阅 Redis的发布/订阅(Pub/Sub)功能是一种消息传递模式,用于实现消息发布者(publisher)和订阅者(subscriber)之间的消息通信。在这种模式下,消息的发送者(发布者)将消息发送到特定的频道(channel),而订阅了该频道的接收者(订阅者)将会接收到这些消息…...
4核8g服务器能支持多少人访问?
腾讯云4核8G服务器支持多少人在线访问?支持25人同时访问。实际上程序效率不同支持人数在线人数不同,公网带宽也是影响4核8G服务器并发数的一大因素,假设公网带宽太小,流量直接卡在入口,4核8G配置的CPU内存也会造成计算…...
【Android】切换系统全局语言设置
前两种为应用内部处理,第三种为发送广播由系统服务进行处理 使用反射 这种会直接将安卓设置内的语言列表清空,然后将选择的语言设置为系统语言 该方法存在问题,在首次开机后设置会导致国外应用进不去(只对于here地图个别版本) /*** 设置语言…...
【递归】【回溯】Leetcode 112. 路径总和 113. 路径总和 II
【递归】【回溯】Leetcode 112. 路径总和 113. 路径总和 II 112. 路径总和解法:递归 有递归就有回溯 记得return正确的返回上去 113. 路径总和 II解法 递归 如果需要搜索整棵二叉树,那么递归函数就不要返回值 如果要搜索其中一条符合条件的路径ÿ…...
AxureCloud配置文件详细介绍
AxureCloud配置文件详细介绍 原文地址:https://docs.axure.com/axure-cloud/business/custom-settings-json/ 通过修改 customsettings.json 可以修改AxureCloud私有部署的域名、端口、HTTPS、存储目录、是否开启插件等, 默认安装的路径为: C:\Program Files\Axure…...
Centos开机网卡自启动失败
问题背景 每次都要手动启动在这里插入代码片 解决方案: 关闭 NetworkManager 服务 systemctl disable NetworkManager systemctl stop NetworkManager重启就会发现网卡已经可以自动启动了...
华为OD技术面试案例3-2024年
技术一面: 1.手撕代码,算法题: 【最小路径和】 手撕代码通过,面试官拍了照片 2.深挖项目,做过的自认为最好的一个项目,描述做过的项目的工作过程,使用到哪些技术? 技术二面&…...
全面升级!Apache HugeGraph 1.2.0版本发布
图数据库以独特的数据管理和分析能力,在企业数智化转型的过程中正在成为数据治理的核心,根据IDC调研显示,95%的企业认为图数据库是重要的数据管理工具,超过65%的厂商认为在业务上图数据库优于其他选择,尤其是在金融风控…...
WinCC如何与三菱Q系列PLC进行以太网通讯
本文主要描述人机界面WinCC如何与三菱Q系列PLC进行以太网通讯,主要介绍了CPU自带以太网口和扩展以太网模块两种情况以及分别使用TCP、UDP两种协议进行通讯组态步骤及其注意事项。 一、 说明 WinCC从V7.0 SP2版本开始增加了三菱以太网驱动程序,支持和三…...
Spring11、整合Mybatis
11、整合Mybatis 步骤: 导入相关jar包 junit <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version> </dependency> mybatis <dependency><groupId>org.my…...
C语言练习:(力扣645)错误的集合
题目链接:645. 错误的集合 - 力扣(LeetCode) 集合 s 包含从 1 到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字…...
广和通发布基于MediaTek T300平台的RedCap模组FM330系列及解决方案
世界移动通信大会MWC 2024期间,广和通发布基于MediaTek T300平台的RedCap模组FM330系列,加速5G-A繁荣发展。FM330系列及其解决方案采用全球先进RedCap方案,满足移动宽带和工业互联对高能效的需求。 广和通FM330系列采用全球首款6nm制程且集成…...
代码随想录训练营第六十三天打卡|503.下一个更大元素II 42. 接雨水
503.下一个更大元素II 1.暴力法,和每日温度那一题如出一辙,循环数组用了一个取模运算就解决了。 class Solution { public:vector<int> nextGreaterElements(vector<int>& nums) {int n nums.size();vector<int> result;for (i…...
【web】nginx+php环境搭建-关键点(简版)
一、nginx和php常用命令 命令功能Nginxphp-fpm启动systemctl start nginxsystemctl start php-fpm停止systemctl stop nginxsystemctl stop php-fpm重启systemctl restart nginxsystemctl restart php-fpm查看启动状态systemctl status nginxsystemctl status php-fpm开机自启…...
1、什么是ETF?
ETF是Exchange Traded Fund的英文缩写,中文称为“交易型开放式指数基金”,又称“指数股”。ETF是一种指数投资工具,通过复制标的指数来构建跟踪指数变化的组合证券,使得投资者通过买卖一种产品就实现了一揽子证券的交易。简单来说…...
备战蓝桥杯Day18 - 双链表
一、每日一题 蓝桥杯真题之工作时长 这个题写代码做的话很麻烦,而且我也不一定能写出来,所以我直接就是用的excel来计算的时间和。 使用excel的做法 1.先把文件中的时间复制到excel中。 2.把日期和时间分到两列。 分成两列的步骤: 选中要…...
【大数据】Flink 内存管理(二):JobManager 内存分配(含实际计算案例)
《Flink 内存管理》系列(已完结),共包含以下 4 篇文章: Flink 内存管理(一):设置 Flink 进程内存Flink 内存管理(二):JobManager 内存分配(含实际…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...
企业大模型服务合规指南:深度解析备案与登记制度
伴随AI技术的爆炸式发展,尤其是大模型(LLM)在各行各业的深度应用和整合,企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者,还是积极拥抱AI转型的传统企业,在面向公众…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...
Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...















