【数据结构】单链表:头部操作我很行,插入也不用增容!!!
单链表


文章目录
- 单链表
- 1.链表
- 1.1链表的概念和结构
- 1.2链表的分类
- 2.单链表的模拟实现
- 2.1单链表的打印
- 2.2单链表的尾插
- 2.3单链表的头插
- 2.4单链表的尾删
- 2.5单链表的头删
- 2.6单链表的查找
- 2.7单链表的中间插入(在结点前插入)
- 2.8单链表的中间删除(删除该结点)
- 2.9单链表的中间插入(在结点后插入)
- 2.10单链表的中间删除(删除该结点的后继)
- 2.11单链表的销毁
- 3.单链表的优缺点
- 4. 链表的经典OJ题目训练
1.链表
1.1链表的概念和结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。


现实中 数据结构中

1.2链表的分类
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构(
2*2*2):
单向或者双向
带头或者不带头(哨兵位的头结点)
循环或者非循环
虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。这种结构在笔试面试中出现很多。
- 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势。
2.单链表的模拟实现
SList.h中
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLTDateType;//单链表结点结构
typedef struct SLTNode
{SLTDateType data;struct SLTNode* next;
}SLTNode;//单链表打印
void SLTPrint(SLTNode* phead);//尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDateType x);//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x);
//结点前插入
void SLTInsert(SLTNode** pphead,SLTNode* pos, SLTDateType x);
//删除该结点
void SLTErase(SLTNode** pphead, SLTNode* pos);//结点后插入
void SLTInsertAfter(SLTNode* pos, SLTDateType x);
//删除该结点的后继
void SLTEraseAfter(SLTNode* pos);
//单链表的销毁
void SLTDestroy(SLTNode** pphead);//free完所有结点,将phead置空
SList.c中
💡ps:单链表是使用一个SLTNode*的指针plist(头指针)来维护的。
⚠注意1:
由于要通过函数接口来实现单链表的增删查改(修改单链表),就要进行址传递,传入&plist
对于不需要修改单链表的接口,直接传入plist即可
⚠注意2:
由于单链表的初始化比较简单,我们在测试文件中(test.c)中,直接
SLTNode* plist = NULL即可单链表的销毁并不是简单地对头指针进行
free,而是要对每个结点进行free
2.1单链表的打印
void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}
💡ps:由于一个结点存着下一个结点的地址,所以我们可以通过cur = cur->next的方式,来找下一个结点,遍历下去。
2.2单链表的尾插
SLTNode* BuyNewnode(SLTDateType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail\n");return NULL;}else{newnode->data = x;newnode->next = NULL;}return newnode;
}//链表的插入操作是不需要对phead进行判空的(因为空链表,phead就是NULL)
//尾插
void SLTPushBack(SLTNode** pphead, SLTDateType x)
{assert(pphead);//防止使用者传参时,传的plist而不是&plist特殊情况,如果是空链表,找不到尾结点,所以直接将newnode作为phead//1.构造新结点SLTNode* newnode = BuyNewnode(x);//特殊情况:空链表if (*pphead == NULL){*pphead = newnode;return;}else//一般情况:非空链表{//2.找到尾结点SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}//3.嫁接tail->next = newnode;}}
💡ps:对于单链表的尾部操作,需要先找到尾结点。时间复杂度:O(N)。
而且对于空链表的情况无法找到尾结点,需要特殊处理。
2.3单链表的头插
//头插
void SLTPushFront(SLTNode** pphead, SLTDateType x)
{assert(pphead);SLTNode* newnode = BuyNewnode(x);newnode->next = *pphead;*pphead = newnode;SLTInsert(pphead, *pphead, x);
}
💡ps:头部操作只需要注意连接关系即可,时间复杂度为:O(1)
2.4单链表的尾删
//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead);assert(*pphead);//删除操作,链表不可为空//特殊情况,如果链表只有一个结点是找不到前驱的,要进行特殊处理SLTNode* tail = *pphead;if (tail->next == NULL)//1个结点的情况{free(tail);*pphead = NULL;}else//多个结点的情况{SLTNode* prev = NULL;//1.找到尾结点和尾结点的前驱while (tail->next != NULL){prev = tail;//每次去下一个,用prev记录下来tail = tail->next;}//2.删除尾结点,进行嫁接和NULLfree(tail);tail = NULL;prev->next = NULL;}}
💡ps:找尾,时间复杂度为O(N)
由于尾删需要找尾结点的前驱,而只有一个结点的链表无法找到尾结点前驱,需要进行特殊处理。
2.5单链表的头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* first = *pphead;*pphead = first->next;free(first);
}
💡ps:时间复杂度为O(N)
2.6单链表的查找
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x)
{SLTNode* cur = phead;while (cur){if (cur->data == x){return cur;}else{cur = cur->next;}}return NULL;
}
2.7单链表的中间插入(在结点前插入)
//结点前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{assert(pphead);assert(pos);SLTNode* newnode = BuyNewnode(x);//特殊情况//头插if (pos == *pphead){SLTPushFront(pphead, x);}SLTNode* prev = *pphead;//1.找pos的前驱while (prev->next != pos){prev = prev->next;}//2.嫁接prev->next = newnode;newnode->next = pos;
}
💡ps:由于需要找结点pos的前驱,时间复杂度O(N)
当pos为首结点时,无法找到pos的前驱需要进行特殊处理(头插操作)
2.8单链表的中间删除(删除该结点)
//删除该结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(*pphead);assert(pos);//特殊情况//头删if (*pphead == pos){SLTPopFront(pphead);}SLTNode* prev = *pphead;//1.找前驱while (prev->next != pos){prev = prev->next;}//2.嫁接prev->next = pos->next;free(pos);
}
💡ps:由于需要找结点pos的前驱,时间复杂度O(N)
当pos为首结点时,无法找到pos的前驱需要进行特殊处理(头删操作)
2.9单链表的中间插入(在结点后插入)
//结点后插入
void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{assert(pos);SLTNode* newnode = BuyNewnode(x);newnode->next = pos->next;pos->next = newnode;
}
2.10单链表的中间删除(删除该结点的后继)
//删除该结点的后继
void SLTEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);//不能尾删SLTNode* next = pos->next;pos->next = next->next;free(next);next = NULL;
}
2.11单链表的销毁
void SLTDestroy(SLTNode** pphead)//free完所有结点,将phead置空
{SLTNode* cur = *pphead;while (cur){SLTNode* next = cur->next;free(cur);cur = next;}*pphead = NULL;
}
3.单链表的优缺点
优点:
1. 头部操作,效率很高,时间复杂度为O(1)
2. 结点之间不是连续存储的,在进行插入操作时,无法考虑增容。
缺点:
1. 不支持随机访问,即使知道是第几个结点,也要进行遍历才能找到该结点。
2. 尾部操作要找结点,效率低下(如果使用一个直接来指向尾结点,也可实现O(1)的效率)
💡ps:对于空链表进行尾部操作时,需要进行特殊处理,这个时候我们可以构造一个哨兵位的头结点,使问题简化。
4. 链表的经典OJ题目训练
-
删除链表中等于给定值 val 的所有结点。 OJ链接
-
反转一个单链表。 OJ链接
-
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。OJ链接
-
输入一个链表,输出该链表中倒数第k个结点。 OJ链接
-
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。OJ链接
-
编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。OJ链接
-
链表的回文结构。OJ链接
-
输入两个链表,找出它们的第一个公共结点。OJ链接
-
给定一个链表,判断链表中是否有环。 OJ链接
-
给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL OJ链接
-
给定一个链表,每个结点包含一个额外增加的随机指针,该指针可以指向链表中的任何结点或空结点。
要求返回这个链表的深度拷贝。OJ链接
-
其他 。ps:链表的题当前因为难度及知识面等等原因还不适合我们当前学习,以后大家自己下去以后 Leetcode OJ链接 + 牛客 OJ链接
相关文章:
【数据结构】单链表:头部操作我很行,插入也不用增容!!!
单链表 文章目录单链表1.链表1.1链表的概念和结构1.2链表的分类2.单链表的模拟实现2.1单链表的打印2.2单链表的尾插2.3单链表的头插2.4单链表的尾删2.5单链表的头删2.6单链表的查找2.7单链表的中间插入(在结点前插入)2.8单链表的中间删除(删除该结点)2.9单链表的中间插入(在结点…...
SpringBoot——使用WebSocket功能
springboot自带websocket,通过几个简单的注解就可以实现websocket的功能; 启动类跟普通的springboot一样: /*** 2023年3月2日下午4:16:57*/ package testspringboot.test7websocket;import org.springframework.boot.SpringApplication; im…...
博弈论小课堂:非零和博弈(实现双赢)【纳什均衡点】
文章目录 引言I 非零和博弈1.1 囚徒问题1.2 博弈中双方的收益矩阵II 在现实中找均衡点2.1 博弈通常不是一次性的,而是反复进行的2.2 博弈论讲的都是阳谋的策略2.3 人类还处于文明的初级阶段,人的道德水准不容高估2.4 乌合之众效应2.5 很多时候看似是双赢,其实是在更大范围内…...
数组中的逆序对
解题思路1: 看到这个题目,我们的第一反应是顺序扫描整个数组。每扫描到一个数组的时候,逐个比较该数字和它后面的数字的大小。如果后面的数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数字。由于每个数字都要和…...
C++基础了解-01-基础语法
基础语法 一、基础语法 C 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 -…...
phpmyadmin 文件包含(CVE-2014-8959)
0x01 漏洞介绍 phpMyAdmin是phpMyAdmin团队开发的一套免费的、基于Web的MySQL数据库管理工具。该工具能够创建和删除数据库,创建、删除、修改数据库表,执行SQL脚本命令等。phpMyAdmin的GIS编辑器中libraries/gis/GIS_Factory.class.php脚本存在目录遍历漏洞。远程攻击者可借助…...
SpringBoot集成MyBatis
目录 实现步骤 1. 在项目的 pom.xml 配置文件中引入如下依赖 2. 在项目的 application.properties 配置文件中添加如下依赖 3. 新建 UserMapper.class 接口类,添加如下 3 个方法 4. 在 /resources/mybatis/mapper 路径(需要手动创建文件夹)下创建 UserMapper.xm…...
MySQL-索引
索引介绍索引是对数据库表中一列或者多列的值进行排序的一种结构,使用索引可提高数据库中特定数据的查询速度。索引是一个单独的、存储在磁盘上的数据库结构,它们包含着对数据表里所有记录的引用指针。使用索引用于快速找出在某个或多个列中有一特定值得…...
【STM32存储器映射-寄存器基地址-偏移】
前言 在学习STM32的时候,我们看到很多的寄存器编程, 比方说LED灯: //GPIOB.5端口输出高电平GPIOB->ODR|1<<5; //PB.5 输出高GPIOE->ODR|1<<5; //PE.5输出高 //GPIOB端口全部输出高电平*(unsigned int*)(0x4001 …...
【华为OD机试2023】最多颜色的车辆 C++ Java Python
【华为OD机试2023】最多颜色的车辆 C++ Java Python 前言 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能最优),不能保证通过率。 Tips1:机试为ACM 模式 你的代码需要处理输入输出,input/cin接收…...
特斯拉后端面试(部分)
HR告知如果面试通过要转.net-_- round1 有没有用过Java新版本,知道有哪些特性吗?A:没有。Q:我们基本在用JDK11,有的新项目用到JDK17了。参考答案1: ZGC: A Scalable Low-Latency Garbage Collector Epsi…...
【python】使用python将360个文件夹里的照片,全部复制到指定的文件夹中,并且按照顺序重新命名
最近要做一个图像生成的课题,在网上找了一个混合的数据集。这个数据集中一共有360个文件夹,然后文件夹中有6-9张不等的照片,我的目标就是编写python代码将所有的照片取出来,放到一个指定的文件夹里,并且从1开始按照顺序…...
【C语言】3天速刷C语言(初识)
【声明】本篇博客只用于对与刚学习C语言的同学的一个初始了解,具体内容请继续关注本专栏后续内容。什么是C语言C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及…...
如何搞定MySQL锁(全局锁、表级锁、行级锁)?这篇文章告诉你答案!太TMD详细了!!!
概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&…...
云计算生态该怎么做?阿里云计算巢打了个样
2023 年 2 月 23 日至 24 日,由阿里云主办的「阿里云计算巢加速器」于杭州阿里云谷园区集结。 阿里云计算巢加速器于 2022 年 8 月正式启动招募,最终百奥利盟、极智嘉、EMQ、KodeRover、MemVerge 等 30 家创新企业入选计算加速器,覆盖了人工智…...
小樽C++ 多章⑧ (贰) 指针与数组
目录 1.C中数组变量名某些情况可以看成是指针 2.C语言的scanf 输入语句,printf 输出语句 3.用指针来当动态数组 小樽C 多章⑧ (壹) 指针变量https://blog.csdn.net/weixin_44775255/article/details/129031168 小樽C 多章⑧ (叁) 指针与字符串、(肆) 函数与指针…...
MXNet的机器翻译实践《编码器-解码器(seq2seq)和注意力机制》
机器翻译就是将一种语言翻译成另外一种语言,输入和输出的长度都是不定长的,所以这里会主要介绍两种应用,编码器-解码器以及注意力机制。编码器是用来分析输入序列,解码器用来生成输出序列。其中在训练时,我们会使用一些…...
RK3588平台开发系列讲解(同步与互斥篇)自旋锁介绍
平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、自旋锁介绍二、自旋锁相关的函数1、普通场景2、进程上下文和下半部3、中断相关三、相关结构体四、函数实现1、初始化2、获取自旋锁沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇介绍自旋锁的使用和基…...
Linux系统CPU占用率较高问题排查思路
作为工程师,在日常工作中我们会遇到 Linux服务器上出现CPU负载达到100%居高不下的情况,如果CPU 持续跑高,则会影响业务系统的正常运行,带来企业损失。对于CPU过载问题通常使用以下两种方式即可快速定位:方法一第一步&a…...
源码解析——HashMap
源码解析——HashMap1. 什么 是HashMap2. 为什么要使用HashMap3. HashMap的使用4. 源码解析4.1 关键问题4.1 存储结构4.2 HashMap 的容量和负载因子4.3 初始化过程4.3 put() 方法实现原理4.3.1 hash()4.3.2 resize()4.4 get() 方法实现原理5. 面试题总结6. ConcurrentHashmap(J…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
在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…...



