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

C语言之旅:探索单链表

目录

一、前言

二、实现链表的功能:

打印

创建节点

尾插

尾删

头插

头删

查找

在指定位置之前插入数据

指定位置删除

在指定位置之后插入数据

打印

销毁

三、全部源码: 

四、结语


一、前言

链表是一个强大且基础的数据结构。对于很多初学者来说,链表可能是一个令人望而生畏的主题,因为它涉及到了动态内存分配和指针操作等较为高级的概念。但是,一旦你掌握了链表的基本概念和操作,你会发现它在很多实际应用中都有着广泛的应用。

今天,我们就从C语言的角度,来探索一下单链表的基本概念和实现方法。在接下来的篇幅中,我将带你逐步构建一个单链表的简单实现,并解释其中的关键概念和代码逻辑。 

首先,我们需要明确什么是单链表。单链表是一种线性数据结构,它的元素(在C语言中通常称为节点)按照线性的顺序排列,但每个节点并不是在内存中连续存储的。相反,每个节点都包含两个部分:一个是数据域(用于存储实际的数据),另一个是指针域(用于指向下一个节点)。通过这种方式,我们可以将多个节点连接起来,形成一个链状的数据结构。

单链表的定义:
 

typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;struct SListNode* next;//
}SLTNode;

二、实现链表的功能:

//打印
void SLTPrint(SLTNode* phead);//头部插入删除/尾部插入删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);

打印

void SLTPrint(SLTNode* phead)
{SLTNode* pucr = phead;while (pucr != NULL){printf("%d->", pucr->data);pucr = pucr->next;}printf("NULL\n");
}

创建节点

SLTNode* SLTBuyNode(SLTDataType x) 
{SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));if (newNode == NULL) {perror("malloc fail!");exit(1);}newNode->data = x;newNode->next = NULL;return newNode;
}

尾插

void SLTPushBack(SLTNode** pphead, SLTDataType x) 
{SLTNode* newNode = SLTBuyNode(x);// 如果链表为空,直接让头指针指向新节点  if (*pphead == NULL) {*pphead = newNode;return;}// 否则,找到链表尾部并插入新节点  SLTNode* current = *pphead;while (current->next != NULL){current = current->next;}current->next = newNode;
}

尾删

//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* prev = NULL;SLTNode* current = *pphead;while (current->next != NULL){prev = current;current = current->next;}free(current);if (prev == NULL){*pphead = NULL;}else{prev->next = NULL;}
}

1.  首先,检查传入的二级指针和其指向的头指针是否为空,以确保链表存在。
2.  接着,定义了两个指针prev和current,用于遍历链表并找到尾节点。
在循环中,prev总是指向current的前一个节点,而current则遍历链表直到找到尾节点(即current->next为NULL的节点)。
3.  找到尾节点后,释放其内存。
4.  最后,根据prev是否为空来判断链表是否只有一个节点。如果是,则将头指针置为NULL;否则,将prev的next指针置为NULL,完成尾节点的删除。 

头插

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newNode = SLTBuyNode(x);newNode->next = *pphead;*pphead = newNode;}

我们首先通过断言确保传入的二级指针pphead不为空。接着,我们调用SLTBuyNode函数来根据给定的数据x创建一个新的节点newNode。然后,我们将新节点的next指针设置为当前的头节点*pphead,这样新节点就连接到了链表的开始位置。最后,我们更新头指针*pphead,使其指向新节点,从而完成了在链表头部插入新节点的操作。 

头删

//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* tmp = (*pphead)->next;free(*pphead);*pphead=tmp;
}

定义一个临时指针tmp,让它指向当前头节点的下一个节点。接着,我们释放当前头节点的内存。最后,我们更新头指针*pphead,使其指向tmp, 从而完成了头节点的删除操作。如果链表原本只有一个节点,那么删除后头指针*pphead将被设置为NULL,表示链表为空。

查找

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;while (pcur != NULL){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

使用一个指针pcur来遍历链表,从头节点开始,逐个检查每个节点的数据是否与要查找的数据x相等。如果找到了匹配的节点,就返回该节点的指针;如果遍历完整个链表都没有找到匹配的节点,则返回NULL表示未找到。

在指定位置之前插入数据

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTBuyNode(x);if (pos == *pphead){SLTPushFront(pphead, x);}else{// 找到pos节点的前一个节点  SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}// 在pos之前插入新节点  newnode->next = pos;prev->next = newnode;}
}

指定位置删除

void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);if (pos == *pphead){*pphead = pos->next;}else{SLTNode* prev = *pphead;while (prev->next != pos)// 遍历链表,找到要删除节点的前一个节点{prev = prev->next;}prev->next = pos->next;}}

在指定位置之后插入数据

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;}

设置新节点的next指针指向pos的下一个节点。最后,它将pos的next指针指向新节点,从而将新节点插入到pos之后。 

打印

//打印
void SLPrint(SL* ps)
{for (int i = 0; i < ps->size; i++) {printf("%d ", ps->arr[i]);}printf("\n");
}

销毁

//销毁
void SListDesTroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur != NULL){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

三、全部源码: 

// 打印链表
void SLTPrint(SLTNode* phead)
{SLTNode* pucr = phead;while (pucr != NULL){printf("%d->", pucr->data);pucr = pucr->next;}printf("NULL\n");
}
//初始化
SLTNode* SLTBuyNode(SLTDataType x) 
{SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));if (newNode == NULL) {perror("malloc fail!");exit(1);}newNode->data = x;newNode->next = NULL;return newNode;
}
// 尾部插入  
void SLTPushBack(SLTNode** pphead, SLTDataType x) 
{SLTNode* newNode = SLTBuyNode(x);// 如果链表为空,直接让头指针指向新节点  if (*pphead == NULL) {*pphead = newNode;return;}// 否则,找到链表尾部并插入新节点  SLTNode* current = *pphead;while (current->next != NULL){current = current->next;}current->next = newNode;
}
//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* prev = NULL;SLTNode* current = *pphead;while (current->next != NULL){prev = current;current = current->next;}free(current);if (prev == NULL){*pphead = NULL;}else{prev->next = NULL;}
}//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newNode = SLTBuyNode(x);newNode->next = *pphead;*pphead = newNode;}
//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* tmp = (*pphead)->next;free(*pphead);*pphead=tmp;
}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;while (pcur != NULL){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTBuyNode(x);if (pos == *pphead){SLTPushFront(pphead, x);}else{// 找到pos节点的前一个节点  SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}// 在pos之前插入新节点  newnode->next = pos;prev->next = newnode;}
}
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);if (pos == *pphead){*pphead = pos->next;}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;}}
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;}//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{assert(pos&&pos->next);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;	}
//销毁链表
void SListDesTroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;while (pcur != NULL){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

四、结语

总之,C语言中的单链表之旅是一次富有成效的学习经历。它不仅让我们掌握了链表的基本操作,还提高了我们的编程技能和问题解决能力。希望这个旅程能为你在数据结构和算法领域的进一步探索打下坚实的基础。

相关文章:

C语言之旅:探索单链表

目录 一、前言 二、实现链表的功能&#xff1a; 打印 创建节点 尾插 尾删 头插 头删 查找 在指定位置之前插入数据 指定位置删除 在指定位置之后插入数据 打印 销毁 三、全部源码&#xff1a; 四、结语 一、前言 链表是一个强大且基础的数据结构。对于很多初…...

【安卓基础】-- 消息机制 Handler

目录 消息机制 Handler面试问题 消息机制 Handler 对handler机制的基本作用、用法、时序流程进行介绍&#xff0c;针对handler机制中的内存泄漏问题讲解&#xff1a;一篇读懂Android Handler机制 Android-Handler机制详解 全面解析 | Android之Handler机制 需要掌握的&#x…...

Optional 类

概述 到目前为止&#xff0c;臭名昭著的空指针异常是导致 Java 应用程序失败的最常见原因。以前&#xff0c;为了解决空指针异常&#xff0c;Google 公司著名的 Guava 项目引入了 Optional 类&#xff0c; Guava 通过使用检查空值的方式来防止代码污染&#xff0c;它鼓励程序员…...

自动微分技术在 AI for science 中的应用

本文简记我在学习自动微分相关技术时遇到的知识点。 反向传播和自动微分 以 NN 为代表的深度学习技术展现出了强大的参数拟合能力&#xff0c;人们通过堆叠固定的 layer 就能轻松设计出满足要求的参数拟合器。 例如&#xff0c;大部分图神经网络均基于消息传递的架构。在推理…...

ASM OMF single-file creation form 重命名

OMF下不能自动命名&#xff0c;需要重新命名的话&#xff1a;1 1. spfile 可以 create pfile from spfile 后再create spfile from pfile 2 redo&#xff1f; 3 datafile&#xff1f; Here are some details of the copy problem: a) You are not allowed to set the numbe…...

VGGNet

VGGNet CNN卷积网络的发展史 1. LetNet5(1998) 2. AlexNet(2012) 3. ZFNet(2013) 4. VGGNet(2014) 5. GoogLeNet(2014) 6. ResNet(2015) 7. DenseNet(2017) 8. EfficientNet(2019) 9. Vision Transformers(2020) 10. 自适应卷积网络(2021) 上面列出了发展到现在CNN的一些经典…...

SpringMVC:转发和重定向

1. 请求转发和重定向简介 参考该链接第9点 2. forward 返回下一个资源路径&#xff0c;请求转发固定格式&#xff1a;return "forward:资源路径"如 return "forward:/b" 此时为一次请求返回逻辑视图名称 返回逻辑视图不指定方式时都会默认使用请求转发in…...

961操作系统知识总结

部分图片可能无法显示&#xff0c;参考这里&#xff1a;https://zhuanlan.zhihu.com/p/701247894 961操作系统知识总结 一 操作系统概述 1. 操作系统的基本概念 重要操作系统类型&#xff1a;批处理操作系统(批量处理作业&#xff0c;单道批处理/多道批处理系统&#xff0c;用…...

电脑死机问题排查

情况描述&#xff1a;2024年6月2日下午16&#xff1a;04分电脑突然花屏死机&#xff0c;此情况之前遇到过三次&#xff0c;认为是腾讯会议录屏和系统自带录屏软件冲突导致。 报错信息&#xff1a;应用程序-特定 权限设置并未向在应用程序容器 不可用 SID (不可用)中运行的地址…...

百度地图1

地图的基本操作 百度地图3.0文档 百度地图3.0实例中心 设置地图 centerAndZoom(center: Point, zoom: Number)设初始化地图,center类型为Point时&#xff0c;zoom必须赋值&#xff0c;范围3-19级&#xff0c; // 百度地图API功能var map new BMap.Map("map"); //…...

Ubuntu 24.04 LTS 安装Docker

1 更新软件包索引&#xff1a; sudo apt-get update 2 安装必要的软件包&#xff0c;以允许apt通过HTTPS使用仓库&#xff1a; sudo apt-get install apt-transport-https ca-certificates curl software-properties-common 3 添加Docker的官方GPG密钥&#xff1a; curl -fs…...

【架构设计】Java如何利用AOP实现幂等操作,防止客户端重复操作

1实现方案详解 在Java中,使用AOP(面向切面编程)来实现幂等操作是一个常见的做法,特别是当你想在不修改业务代码的情况下添加一些横切关注点(如日志、事务管理、安全性等)时。幂等操作指的是无论执行多少次,结果都是相同的操作。 为了利用AOP实现幂等操作以防止客户端重…...

笔记:美团的测试

0.先启动appium 1.编写代码 如下&#xff1a; from appium import webdriver from appium.webdriver.extensions.android.nativekey import AndroidKeydesired_caps {platformName: Android,platformVersion: 10,deviceName: :VOG_AL10,appPackage: com.sankuai.meituan,ap…...

【30天精通Prometheus:一站式监控实战指南】第15天:ipmi_exporter从入门到实战:安装、配置详解与生产环境搭建指南,超详细

亲爱的读者们&#x1f44b;   欢迎加入【30天精通Prometheus】专栏&#xff01;&#x1f4da; 在这里&#xff0c;我们将探索Prometheus的强大功能&#xff0c;并将其应用于实际监控中。这个专栏都将为你提供宝贵的实战经验。&#x1f680;   Prometheus是云原生和DevOps的…...

STM32F103借助ESP8266连接网络

ESP8266配置 STM32F103本身是不具备联网功能的&#xff0c;所以我们必须借助其他单片机来进行联网&#xff0c;然后让STM32与联网单片机通信&#xff0c;就可以实现STM32联网了。 本文借助的是ESP8266模块&#xff0c;其通过UART协议与STM32通信&#xff08;http://t.csdnimg.c…...

Feature Manipulation for DDPM based Change Detection

基于去噪扩散模型的特征操作变化检测 文章提出了一种基于去噪扩散概率模型&#xff08;DDPM&#xff09;的特征操作变化检测方法。变化检测是计算机视觉中的经典任务&#xff0c;涉及分析不同时间捕获的图像对&#xff0c;以识别场景中的重要变化。现有基于扩散模型的方法主要…...

第十三届蓝桥杯国赛大学B组填空题(c++)

A.2022 动态规划 AC; #include<iostream> #define int long long using namespace std; int dp[2050][15]; //dp[i][j]:把数字i分解为j个不同的数的方法数 signed main(){dp[0][0]1;for(int i1;i<2022;i){for(int j1;j<10;j){//一种是已经分成j个数,这时只需每一个…...

conda源不能用了的问题

conda旧没用了&#xff0c;不知道什么原因&#xff0c;安装源出问题&#xff0c;报如下错&#xff1a; Loading channels: failedUnavailableInvalidChannel: HTTP 404 NOT FOUND for channel anaconda/pkgs/main <https://mirrors.aliyun.com/anaconda/pkgs/main>The c…...

【C#】自定义List排序规则的两种方式

目录 1.系统排序原理 2.方式一&#xff1a;调用接口并重写 3.方式二&#xff1a;传排序规则函数做参数 1.系统排序原理 当我们对一个List<int>类型的数组如list1排序时&#xff0c;一个轻松的list1.sort();帮我们解决了问题 但是在实际应用过程中&#xff0c;往往我们…...

ANAH数据集- 大模型幻觉细粒度评估工具

大型语言模型&#xff08;LLMs&#xff09;在各种自然语言处理任务中取得了显著的性能提升。然而&#xff0c;它们在回答用户问题时仍面临一个令人担忧的问题&#xff0c;即幻觉&#xff0c;它们会产生听起来合理但不符合事实或无意义的信息&#xff0c;尤其是当问题需要大量知…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...