【数据结构-单链表】(C语言版本)

今天分享的是数据结构有关单链表的操作和实践(图解法,图变化更利于理解)

记录宗旨📝: 眼(脑)过千遍,不如手过一遍。

我们都知道单链表是一种常见的链表数据结构,由一系列节点组成。每个节点包含两部分:数据域和指针域。

  • 数据域:用于存储节点中的数据。
  • 指针语:用于指向下一个节点的地址。

每个节点的指针域指向链表中的下一个节点,最后一个节点的指针域指向NULL,表示链表的结束。

链表的头节点是链表中的第一个节点。通过头节点,可以访问整个链表。

相比于数组,链表具有动态性,可以根据需要动态地插入和删除节点,而无需预先分配固定大小的内存空间。

Node1       Node2       Node3       Node4
+------+   +------+   +------+   +------+
| data |   | data |   | data |   | data |
+------+   +------+   +------+   +------+
| next |-->| next |-->| next |-->| NULL |
+------+   +------+   +------+   +------+

在上面的示例中,每个节点包含一个数据域(data)和一个指针域(next)。节点1、节点2、节点3和节点4依次连接,形成一个链表。节点4的指针域指向NULL,表示链表的结束。

通过遍历链表,可以访问链表中的所有节点,并执行相应的操作。例如,可以在链表中插入新节点、删除节点或查找特定节点等。

因此以下主要进行单链表的操作进行分析和实践

单链表的结构

在这里插入图片描述

其中head为头节点,head -> length: 链表的长度, head -> next:指向首元节点(储存首元节点的地址)。一次前一个节点指向后一个节点,尾节点最后一个NULL

头文件

#include <stdio.h> // 输入输出
#include <stdlib.h> // malloc相关库使用
#include <stdbool.h> // 工具类

声明链表结构体

通过typedef声明结构体, 并给int类型起别名,便于数据类型变更的维护。

typedef int ElementType;typedef struct s_node
{// 声明节点数据域ElementType data;// 声明下一个节点的指针域struct s_node* next;
} ListNode;

单链表的创建–头插法创建

目前有一条数据,{1,2,3,4,5}。 然后依次添加到链表中,如图:

image.png

在这里插入图片描述

...

在这里插入图片描述

我们可以看到上面的头插法就是将数据每一个新的插入在头节点head的后面,依次往复。

// 头插法创建链表, arr: 需要插入的数据, size: 需要创建链表的长度
ListNode* topInsertList(int arr[], int size) {ListNode* head = (ListNode*)malloc(sizeof(ListNode));head -> next = NULL;ListNode* node;for (int i = 0; i < size; i++){// 为每次新增数据节点创建空间node = (ListNode*)malloc(sizeof(ListNode));// 将数据内容分别给到新创建的空间node -> data = arr[i];// 当前数据的下一个节点为插入前头节点的下一个节点地址node -> next = head -> next;// 将头节点重新赋值为当前节点head -> next = node;}// 返回头节点return head;
}

输出: 5,4,3,2,1, 可以看出来和输入数据相反。

头插法只需要操作两个节点,当前元素指向头节点的指向,并修改头节点指向当前节点。

单链表的创建–尾插法创建

目前有一条数据,{1,2,3,4,5}。 然后依次添加到链表中,如图:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

尾插法更加直接,相当于在一条线的尾巴上进行追加链接数据。

/*创建一个新的链表arr: 创建链表的数据size: 创建的大小尾插法思想: 首先将新插入的节点设置成一个完整的链表节点格式,然后将头节点复制给一个临时节点,每次将临时节点都指向下一个新插入节点,保证节点的移动和链接
*/;
ListNode* rInsertList(int arr[], int size) {ListNode *head = (ListNode*)malloc(sizeof(ListNode));// node为新插入的节点。 middle为链表最后节点,当第一次创建链表时指向头节点ListNode *node, *middle;// head -> next = NULL;// middle临时当作头节点middle = head;for (int i = 0; i < size; i++){node = (ListNode*)malloc(sizeof(ListNode));node -> data = arr[i];node -> next = NULL;middle -> next = node;middle = node;}middle -> next = NULL;return head;
}

输出: 1,2,3,4,5。 输入和输出一样。

尾插法因为需要获取到当前最后一个节点,故需要设置三个节点:头节点,当前节点,动态变化的尾节点。

遍历链表

可以将上面新创建的链表打印输出:

//打印链表
void disList(ListNode *list) {//(1)、当遍历链表没有head头节点的时候使用第一条:ListNode *node = list; // ndoe指向首节点// (2)、当遍历链表由头节点的时候选择第二条//ListNode *node = list -> next; // 头节点指向首元节点while(node != NULL) {printf("%d====", node -> data);node = node -> next;}printf("\n");
}

按照上面方式创建的链表,可以使用这个方法打印输出链表的内容。

获取链表长度

int isLength(ListNode *list) {//(1)、当遍历链表没有head头节点的时候使用第一条:ListNode *node = list;// (2)、当遍历链表由头节点的时候选择第二条//ListNode *node = list -> next; // 头节点指向首元节点// p = p-> next;int n = 0;while(node != NULL) {n++;node = node->next;};printf("长度:%d\n", n);return n;
}

判断是否为空

// 检查线性链表是否为空
int isEmpty(ListNode* list) {return list == NULL;
}

删除链表中指定元素

删除链表中的指定元素,首先需要查找当前元素所在链表中的位置,然后将该节点赋值给一个临时指针,方便修改前后指针的指向:

在这里插入图片描述

// 删除指定元素, 传入的时候,list传入需要传入链表的地址
bool deleteNode(ListNode **list, ElementType key) {// 声明临时指针ListNode *temp;// 判断是否为头节点if ((*list) -> data == key) {// 将头节点复制给临时指针temp = *list;// 并将头节点的指针指向后一个节点*list = (*list) -> next;// 删除头节点空间free(temp);return true;} else {// 将头节点复制给临时ListNode *pre = *list;while(pre -> next != NULL) {if (pre -> next -> data == key) {// 将temp备份为待删除节点temp = pre -> next;pre -> next = pre -> next -> next;free(temp);return true;} else {pre = pre -> next;}}return false;}
}

例如输入:{1,2,3,4,5}, 删除2,结果变成{1,3,4,5}。

可以清晰看到删除操作,在声明指针的时候,声明了三个指针,并且传入的链表头节点采用的双指针,这是为什么呢?

首先声明的临时指针用于存放当前需要删除的节点指针,如果直接删除当前节点,后续节点还未将数据节点指向头节点或者前一个节点,这会导致整个链表后续的节点丢失。当前链表就断开了,这是不允许的。 因此增加临时指针,用于过渡。

反转一个链表

  • 把当前节点的next存起来,next = curr -> next
  • 把当前节点的next指向前一个节点, curr -> next = prev
  • 前指针后移prev = curr
  • 当前指针后移curr = next
ListNode* reversList(ListNode *list) {ListNode *curr = list -> next;ListNode *prev = NULL;while(curr != NULL) {ListNode * next = (ListNode*)malloc(sizeof(ListNode));next = curr -> next;curr -> next = prev;prev = curr;curr = next;}return prev; // 返回反转后的首元节点
}

反转链表的思想就是:指定一个指针,每次给当前指针的下一个值赋值给一个临时变量,防止链表断开从而找不到后续节点;并将当前节点的的下一个指针指向赋值为新设置的节点,第一次新设置的节点为NULL,后续并将操作完的当前节点赋值给设置的节点,保证下次节点的移动从而遍历链表中的数据,依次反转元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/591736.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java进阶篇】String中 intern 的原理是什么?

String中 intern 的原理 ✔️ 典型解析✔️小思考&#xff08;回顾&#xff09; ✔️字面量✔️intern✔️ intern原理✔️a和1有什么不同✔️答案 ✔️ 典型解析 字符串常量池中的常量有两种来源&#xff1a; 1、 字面量会在编译期先进入到Class常量池&#xff0c;然后再在运行…

milvus学习(一)cosin距离和欧式距离

参考&#xff1a;https://blog.csdn.net/qq_36560894/article/details/115408613 归一化以后的cosin距离和欧式距离可以相互转化&#xff0c;未归一化的不可以相互转化&#xff08;因为距离带单位&#xff09;。

C++多态性——(2)联编

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 成功的秘诀就在于多努力一次&#xff…

自动化测试中,如何增加失败重试机制!

01、前言 在执行自动化测试用例时&#xff0c;会发现有时候用例失败并非代码问题&#xff0c;而是由于服务正在发版&#xff0c;导致请求失败&#xff0c;从而降低了自动化用例的稳定性&#xff0c;最后还要花时间定位到底是自身case的原因还是业务逻辑问题&#xff0c;还是其…

电子招标采购系统源码之从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理

​随着市场竞争的加剧和企业规模的扩大&#xff0c;招采管理逐渐成为企业核心竞争力的重要组成部分。为了提高招采工作的效率和质量&#xff0c;我们提出了一种基于电子化平台的解决方案。该方案旨在通过电子化招投标&#xff0c;使得招标采购的质量更高、速度更快&#xff0c;…

宣传照(私密)勿转发

精美的海报通常都是由UI进行精心设计的&#xff0c;现在有100 件商品需要进行宣传推广&#xff0c;如果每个商品都出一张图显然是不合理的&#xff0c;且商品信息各异。因此需要通过代码的形式生成海报。对此&#xff0c;我也对我宣传一波&#xff0c;企图实现我一夜暴富的伟大…

贪心算法part04 860柠檬水找零 406根据身高重建队列

860 柠檬水找零 406 根据身高重建队列 452 用最少数量的箭引爆气球

六、HTML 段落

HTML 可以将文档分割为若干段落。 一、HTML 段落 段落是通过 <p> 标签定义的。 <p>这是一个段落 </p> <p>这是另一个段落</p> 注意&#xff1a;浏览器会自动地在段落的前后添加空行。&#xff08;</p> 是块级元素&#xff09; 二、不…

[蓝桥杯知识学习] 树链

DFS序 什么是DFS序 怎么求DFS序 进入操作&#xff0c;将有计数 出&#xff1a;可以理解为&#xff0c;没有孩子可以去了&#xff08;不能&#xff0c;向下行动&#xff1a;对应于程序里的入栈&#xff09;&#xff0c;所以回到父结点&#xff08;向上行动&#xff0c;对应于程…

SCENIC+:增强子和基因调控网络的单细胞多组学推理

SCENIC&#xff1a;增强子和基因调控网络的单细胞多组学推理 摘要IntroductionSCENIC 使用超过 30,000 个 TF 基序来预测 eGRNSCENIC 在 PBMC 多组数据上的图示SCENIC prioritizes functional enhancers &#xff08;SCENIC 优先考虑功能增强剂&#xff09;SCENIC simulates ph…

从0到1实战,快速搭建SpringBoot工程

目录 一、前言 二、准备工作 2.1 安装JDK 2.2 安装Maven 2.3 下载IDEA 三、从0到1搭建 3.1 创建SpringBoot工程 3.2 运行SpringBoot工程 四、总结 一、前言 SpringBoot是一个在Spring框架基础上构建的开源框架&#xff0c;不仅继承了Spring框架原有的优秀特性&#x…

Selenium自动化测试之学会元素定位

这是我经常被问到的一个问题&#xff0c;也是我很讨厌回答的问题&#xff0c;因为要想回答这个问题需要知道上下文。什么样式的元素&#xff0c;有哪些属性&#xff0c;是否有表单嵌套&#xff0c;都尝试了哪些定位方法。。。而且没几个提问者能事先详细的说明这些。哪儿像提bu…

centos7通过systemctl启动springboot服务代替java -jar方式启动

背景&#xff1a;以前启动jar包都是通过java -jar&#xff0c;后面了解到一种注册服务式启动jar包的方式&#xff0c;所以做个记录。 注意&#xff1a;我在写该篇博客的时候&#xff0c;给脚本代码都加了#注释来解释该行脚本命令的意思&#xff0c;所以可能出现复制该篇博客脚本…

Android 13 - Media框架(29)- MediaCodec(四)

上一节我们了解了如何通过 onInputBufferAvailable 和 getInputBuffer 获取到 input buffer index&#xff0c;接下来我们一起学习上层如何拿到buffer并且向下写数据的。 1、获取 input Buffer 获取 MediaCodec 中的 buffer 有两种方式&#xff0c;一种是调用 getInputBuffers…

Jsonpath - 数据中快速查找和提取的强大工具

JSON&#xff08;JavaScript Object Notation&#xff09;在现代应用程序中广泛使用&#xff0c;但是如何在复杂的JSON数据中 查找和提取所需的信息呢&#xff1f; JSONPath是一种功能强大的查询语言&#xff0c;可以通过简单的表达式来快速准确地定位和提取JSON数据。本文将介…

如何将内容转化为流量?媒介盒子分享

软文营销就是将内容转为流量的常见做法&#xff0c;但是有许多企业在做内容时往往一头雾水导致效果不佳&#xff0c;做内容不是光靠写就可以的&#xff0c;还需要做好选题、类型、结构等&#xff0c;今天媒介盒子就来和大家聊聊&#xff1a;如何将内容转化为流量。 一、 确定内…

【SpringBoot开发】之商城项目案例(购物车相关操作)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发之商城项目系列》。&#x1f3af…

2023最新租号平台系统源码支持单独租用或合租使用

这是一款租号平台源码&#xff0c;采用常见的租号模式。目前网络上还很少见到此类类型的源码。 平台的主要功能如下&#xff1a; 支持单独租用或采用合租模式&#xff1b; 采用易支付通用接口进行支付&#xff1b; 添加邀请返利功能&#xff0c;以便站长更好地推广&#xf…

每天刷两道题——第三天

1.1两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09; 输入&#xff1a;[1,2,3,4] 输出&#xff1a;[2,1,4,3…

祝贺首届兽医专业学位研究生创新创业大赛圆满闭幕

为进一步贯彻落实科教兴国、农业强国发展战略&#xff0c;适应兽医行业、产业发展需求&#xff0c;提高兽医专业学位研究生实践创新能力&#xff0c;推动兽医领域高层次应用型人才培养供给侧改革&#xff0c;促进兽医专业学位教育高质量发展&#xff0c;12月28日由中国学位与研…