数据结构——单链表详解(超详细)(2)
前言:
上一篇文章小编简单的介绍了单链表的概念和一些函数的实现,不过为了保证文章的简洁,小编把它分成了两篇来写,这一篇小编紧接上一篇文章继续写单链表函数功能的实现:

目录:
1.单链表剩余函数的编写
1.1.单链表查找数据
1.2.在指定位置之前插入数据
1.3.删除指定结点
1.4.在指定位置之后插入数据
1.5.删除指定位置之后的结点
1.6.单链表的销毁
2.完整的代码呈现
正文:
1.单链表剩余函数的编写
1.1.单链表查找数据
//查找SLTNode* SLTFind(SLTNode* phead, SLdate x);
对于单链表的查找数据,这就类似顺序表的查找数据,我们要遍历所有的单链表,直到找到我们想要的数据位置,加入没有我们想要的数据,那么我们可以直接返回一个空地址就好了,此时我们不必再传单链表指针的地址了,因为我们本质上并没有对头结点进行改变 ,所以我们仅需把头节点传过去就可以,在函数内部,我们想要遍历顺序表,就要采用循环,来进行一个一个结点的查找,下面是代码呈现:
SLTNode* SLTFind(SLTNode* phead, SLdate x)
{SLTNode* pour = phead;while (pour){if (pour->date == x){return pour;}pour = pour->next;}return NULL;
}
1.2.在指定位置之前插入数据
这里我们可以用到我们上面用到的查找函数,因为它是返回的结点,所以我们可以通过它来确定我们想要的指定位置,因为单链表只可以往后一步一步的走,而不能往前找数据,所以我们得先确认我们想要找的位置,下面我们涉及指定位置的时候,都要引用一下查找指定位置这个函数,下面我们来进行这个函数的讲解,这个函数其实也分为两种情况
第一种情况是指定位置就是头结点,此时这就类似于我们之前写过的头插函数,我们直接引用头插函数就可以实现这个功能。
第二种情况就是普通情况,我们要在指定位置之前插入数据,所以我们需要找到指定位置之前的数据,所以这里我们要用用到头结点,并且用到前面讲的创建新结点的函数(因为此文章涉及了不少插入数据的函数,所以我会把创建新结点的代码再写一遍!),我们让一个结点放入头结点数据,然后一步一步往后走,直到走到指定位置之前,之后我们可以讲新节点的下一个结点设置为指定结点,然后再把之前指定位置之前的结点的下一个结点设置为新结点,这里我们便可以完成在指定位置之后插入数据,下面小编先给上图文解释:

新结点的创建:
SLTNode* SLTbuynode(SLdate x)
{SLTNode* pour = (SLTNode*)malloc(sizeof(SLTNode));assert(pour);pour->date = x;pour->next = NULL;return pour;
}
头插函数:
void SLTPushFront(SLTNode** pphead, SLdate x)
{assert(pphead);SLTNode* newnode = SLTbuynode(x);newnode->next = *pphead;*pphead = newnode;
}
函数部分:
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x)
{assert(pos && pphead && *pphead);if (pos == *pphead){SLTPushFront(pphead,x); //这个是头节点就是pos的特殊情况,我们在指定位置之前插入数据的时候,是会先找到指定位置之前的数据以及之后的,头节点的话找不到数据了}else{SLTNode* newnode = SLTbuynode(x);SLTNode* pour = *pphead;SLTNode* pur = NULL;while (pour != pos){pur = pour;pour = pour->next;}assert(pur);newnode->next = pour;pur->next = newnode;}
1.3.删除指定结点
删除指定结点的操作,同样也和上面的函数一样,我们也分为两种情况进行讨论:
第一种情况就是我们想要删除的指定节点就是头结点,此时我们只需要调用一下头删函数就可以实现。
第二种情况自然就是普通情况,此时我们依旧需要用到头结点,因为我们需要知道指定结点之前的结点,之后我们只需要让前一个结点的下一个结点直接指向指定结点的下一个结点,之后我们在把指定节点给释放掉,就可以实现指定节点删除操作,下面小编先给上图文解释:

头删函数:
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pour = *pphead;*pphead = (*pphead)->next;free(pour);pour = NULL;
}
删除指定结点函数:
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead && pos <= *pphead);if (*pphead == pos){SLTPopFront(pphead);}else{SLTNode* pour = *pphead;SLTNode* pur = NULL;while (pour != pos){pur = pour;pour = pour->next;}assert(pur);pur -> next = pur->next -> next;free(pour);pour = NULL;}
}
1.4.在指定位置之后插入数据
小编在上面已经讲述了指定位置之前如何插入数据了,那么现在可以讲述在指定位置之后插入数据了,这个其实比上面那个简单多了,因为此时我们不需要用到指定位置之前的结点了,所以我们不必在使用头结点了,此时我们依然需要先创建一个新节点,之后我们在讲新结点之后的结点变为我们指定位置之后的结点,指定位置之后的结点·在指向新节点,这里就可以实现在指定位置之后插入新数据了,下面小编先给上图文解释,再给上代码呈现:
void SLTInsertAfter(SLTNode* pos, SLdate x)
{assert(pos);SLTNode* newnode = SLTbuynode(x);newnode->next = pos->next;pos->next = newnode;
}
1.5.删除指定位置之后的结点
有插入必会有删除,下面我们将要讲述最后一个重要的函数,那么就是删除指定位置之后的结点,这个,同样也是非常的简单,我们只需要把指定结点的下一个结点变为下一个的下一个的结点,再把原来指定位置之后的结点释放掉就好了,下面我们直接给出图像说明:

void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* pour = pos -> next;pos->next = pos->next->next;free(pour);pour = NULL;
}
1.6.单链表的销毁
单链表有创建,自然就会有销毁,单链表的销毁也不算很难,毕竟难的部分都在上面,轻舟已过万重山,对于单链表的销毁其实就是把每个结点给free掉,我们可以设置一个指针作为头结点,在设置一个指针用来给予上一个指针,每销毁完一个结点,我们把第二个指针的内容给予第一个指针,然后让第二个指针继续往后走,这样我们通过循环的知识可以做到每个结点的删除,循环结束完以后,别忘了对头结点进行销毁,这样我们便可以完成对于单链表的销毁了,下面直接给上代码:
void SListDestroy(SLTNode** pphead)
{assert(*pphead && pphead);SLTNode* pour = *pphead;while (pour){SLTNode* next = pour->next;free(pour);pour = next;}*pphead = NULL;
}
2.完整的代码呈现
现在,小编已经把单链表大部分内容给讲完了,但单链表实现的功能远不止这些,感兴趣的读者朋友可以再继续探索,小编这里给上大家单链表的完整代码:
SList.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLdate; //方便后面整体类型的改变//创立一个单链表
typedef struct Slist {//先设置一个类型SLdate date;struct Slist* next; //存放下一个节点的地址
}SLTNode;//链表的打印
void SLTprintf(SLTNode* phead);//开始正式环节了
//尾插
void SLTPushBack(SLTNode** pphead, SLdate x);//头插
void SLTPushFront(SLTNode** pphead, SLdate x);//尾删
void SLTPopBack(SLTNode** pphead);//头删void SLTPopFront(SLTNode** pphead);//查找SLTNode* SLTFind(SLTNode* phead, SLdate x);//在指定位置之前插入数据void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x);//删除pos节点void SLTErase(SLTNode** pphead, SLTNode* pos);//在指定位置以后插入数据void SLTInsertAfter(SLTNode* pos, SLdate x);//删除pos之后的节点void SLTEraseAfter(SLTNode* pos);//销毁链表
void SListDestroy(SLTNode** pphead);
SList.c
#include"SList.h"
void SLTprintf(SLTNode* phead)
{SLTNode* pour = phead; //这么做是为了保证头节点不会发生改变while (pour){printf("%d->", pour->date);pour = pour->next;}printf("NULL\n");
} //这个操作是打印单链表的数据SLTNode* SLTbuynode(SLdate x)
{SLTNode* pour = (SLTNode*)malloc(sizeof(SLTNode));assert(pour);pour->date = x;pour->next = NULL;return pour;
}void SLTPushBack(SLTNode** pphead, SLdate x)
{//首先可以先建一个函数,这个函数是来开辟一个新节点的(后面想要插入直接调用就好了)assert(pphead);SLTNode * p = SLTbuynode(x);if (*pphead == NULL) //首先判断{*pphead = p;}else{SLTNode* pour = *pphead;while (pour -> next){pour = pour->next;}pour->next = p;}
}void SLTPushFront(SLTNode** pphead, SLdate x)
{assert(pphead);SLTNode* newnode = SLTbuynode(x);newnode->next = *pphead;*pphead = newnode;
}void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);if ((*pphead)->next == NULL){*pphead = NULL;}else{SLTNode* pour = *pphead;SLTNode* plist = NULL;while (pour->next){plist = pour;pour = pour->next;}plist->next = NULL;free(pour);pour = NULL;}
}void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pour = *pphead;*pphead = (*pphead)->next;free(pour);pour = NULL;
}SLTNode* SLTFind(SLTNode* phead, SLdate x)
{SLTNode* pour = phead;while (pour){if (pour->date == x){return pour;}pour = pour->next;}return NULL;
}void SLTInsert(SLTNode** pphead, SLTNode* pos, SLdate x)
{assert(pos && pphead && *pphead);if (pos == *pphead){SLTPushFront(pphead,x); //这个是头节点就是pos的特殊情况,我们在指定位置之前插入数据的时候,是会先找到指定位置之前的数据以及之后的,头节点的话找不到数据了}else{SLTNode* newnode = SLTbuynode(x);SLTNode* pour = *pphead;SLTNode* pur = NULL;while (pour != pos){pur = pour;pour = pour->next;}assert(pur);newnode->next = pour;pur->next = newnode;}
}void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead && pos <= *pphead);if (*pphead == pos){SLTPopFront(pphead);}else{SLTNode* pour = *pphead;SLTNode* pur = NULL;while (pour != pos){pur = pour;pour = pour->next;}assert(pur);pur -> next = pur->next -> next;free(pour);pour = NULL;}
}void SLTInsertAfter(SLTNode* pos, SLdate x)
{assert(pos);SLTNode* newnode = SLTbuynode(x);newnode->next = pos->next;pos->next = newnode;
}void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);SLTNode* pour = pos -> next;pos->next = pos->next->next;free(pour);pour = NULL;
}void SListDestroy(SLTNode** pphead)
{assert(*pphead && pphead);SLTNode* pour = *pphead;while (pour){SLTNode* next = pour->next;free(pour);pour = next;}*pphead = NULL;
}
总结:
可算快马加鞭的肝完这篇文章了,小编本来想上一篇文章就结束单链表的,但是觉得万字文章有点太多了内容,于是分装成两篇来书写,因为小编是学完了单链表就开始写的文章,有一些地方可能有错误或者文笔显得很繁琐,各位读者朋友见谅,如果有错误的话,恳请在评论区指出,小编会及时的更正,那么,我们下一篇文章见啦!

相关文章:
数据结构——单链表详解(超详细)(2)
前言: 上一篇文章小编简单的介绍了单链表的概念和一些函数的实现,不过为了保证文章的简洁,小编把它分成了两篇来写,这一篇小编紧接上一篇文章继续写单链表函数功能的实现: 目录: 1.单链表剩余函数的编写 1.…...
类和对象(2
*续上文中的运算符重载 4 重载运算符时,有前置和后置的,运算符重载的函数名都是operator, 无法很好区分 所以c规定,后置重载时,增加一个int形参 与前置做区分 5 重载<<和>>时需要重载为全局函数, 因为重载为成员函数时, this指针默认抢占了第一个形参位, 第一个形参…...
AcWing 668. 游戏时间2
读取四个整数 A,B,C,D𝐴,𝐵,𝐶,𝐷,用来表示游戏的开始时间和结束时间。 其中 A𝐴 和 B𝐵 为开始时刻的小时和分钟数,C𝐶 和 D𝐷 为结束时刻的小时和分钟数。…...
AI发展下的伦理挑战,应当如何应对?
针对人工智能(AI)发展中面临的伦理挑战,构建一个全面、有效的治理体系至关重要。以下是对三大关键方向——隐私保护、算法公正与透明度、深度伪造管控——的深入探讨与具体实践方案: 方向一:构建可靠的AI隐私保护机制…...
Java面试题--JVM大厂篇之深入解析JVM中的Serial GC:工作原理与代际区别
目录 引言: 正文: 一、Serial GC工作原理 年轻代垃圾回收(Minor GC): 老年代垃圾回收(Major GC或Full GC): 二、年轻代和老年代的区别 年轻代(Young Generation&a…...
网络安全高级工具软件100套
1、 Nessus:最好的UNIX漏洞扫描工具 Nessus 是最好的免费网络漏洞扫描器,它可以运行于几乎所有的UNIX平台之上。它不止永久升级,还免费提供多达11000种插件(但需要注册并接受EULA-acceptance–终端用户授权协议)。 它…...
Ubuntu 添加gcc头文件搜索路径
对个人用户生效 sudo vim ~/.bashrc在该文件末尾添加 #gcc C_INCLUDE_PATH$C_INCLUDE_PATH:your_path export C_INCLUDE_PATH #g CPLUS_INCLUDE_PATH$CPLUS_INCLUDE_PATH:your_path export CPLUS_INCLUDE_PATH最后,重启终端即可生效 可用以下命令查看搜索路径 ec…...
c++写数据结构进入文件
以下定义一个数据结构 struct SData {std::string url;int number;std::string memo; };写入文件 void StorageDataToFile(const std::string& filename, const SData& data) {std::ofstream outFile(filename);if (outFile.is_open()) {// 使用std::stringstream格式…...
Java实验4
实验内容 考试题 要求在一个界面内至少显示5道选择题,每道题4个选项。题目从数据库读取。表结构自定义。 另有2个命令按钮,分别为“重新答题”(全部选项及正确答题数清空)和“提交”(计算),在…...
优化 Java 数据结构选择与使用,提升程序性能与可维护性
优化 Java 数据结构选择与使用,提升程序性能与可维护性 引言 在软件开发中,数据结构的选择是影响程序性能、内存使用以及代码可维护性的关键因素之一。Java 作为一门广泛使用的编程语言,提供了丰富的内置数据结构,如数组、链表、…...
华为USG6000V防火墙安全策略用户认证
目录 一、实验拓扑图 二、要求 三、IP地址规划 四、实验配置 1🤣防火墙FW1web服务配置 2.网络配置 要求1:DMZ区内的服务器,办公区仅能在办公时间内(9:00-18:00)可以访问,生产区的设备全天可以访问 要求2:生产区不…...
Windows 应急响应手册v1.2 -百度网盘下载
家好,《Windows 应急响应手册 v1.2》 发布啦! 本次是一个大更新,解决了两个大问题,添加了4个大的事件处置流程以及一些更新,下载链接在文末 两个大问题是: Windows 平台的 Adobe acrobat DC 、Firefox 浏…...
Billu_b0x靶机
信息收集 使用arp-scan 生成网络接口地址来查看ip 输入命令: arp-scan -l 可以查看到我们的目标ip为192.168.187.153 nmap扫描端口开放 输入命令: nmap -min-rate 10000 -p- 192.168.187.153 可以看到开放2个端口 nmap扫描端口信息 输入命令&…...
GitHub+Picgo图片上传
Picgo下载,修改安装路径,其他一路下一步! 地址 注册GitHub,注册过程不详细展开,不会的百度一下 地址 新建GitHub仓库存放图片 ——————————————————————————————————————————…...
springboot的事务管理
在yml配置文件中添加以下:logging.level.org.springframework.jdbc.support.JdbcTransactionManagerdebug...
深入解析rsync:定义、架构、原理、应用场景及实战指南
前言 在现代数据管理和传输过程中,数据同步工具起着至关重要的作用。特别是在需要高效、可靠地在不同服务器或设备之间传输大量数据时,选择一款优秀的数据同步工具显得尤为重要。在众多工具中,rsync以其高效、灵活、可靠的特点,成…...
discuz手机版发帖提示“网络出现问题,请稍后再试”
大家好,我是网创有方。今天分享一个discuz发帖报错的问题。 问题描述:discuz手机网页端发帖提示“网络出现问题,请稍后再试”,但是实际上帖子已经发布成功。 本次记录下分析过程: 第一步:打开宝塔&#…...
图片如何去水印,PS 图片去水印的几种常见方法
在数字图像的世界里,水印常常被用来标识版权或防止未经授权的使用,但有时它们却成为了美观的障碍。无论是出于个人偏好还是专业需求,去除图片上的水印已经成为一项常见的任务。 Adobe Photoshop 作为行业标准的图像编辑软件,提供…...
【从零开始实现stm32无刷电机FOC】【实践】【5/6 stm32 adc外设的高级用法】
目录 采样时刻触发采样同步采样 点击查看本文开源的完整FOC工程 本节介绍的adc外设高级用法用于电机电流控制。 从前面几节可知,电机力矩来自于转子的q轴受磁力,而磁场强度与电流成正比,也就是说电机力矩与q轴电流成正相关,控制了…...
2407-mysql笔记
数据库(Database),简称db mariadbmysql 常见的数据库:mysql、oracle、高斯(Gauss)、redis、sqlserver、SQLite、HBase 一、SQL(Structured Query Language):结构化查询语言 1、作用ÿ…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
