【数据结构-单链表】(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.rhkb.cn/news/229265.html

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

相关文章

SpringMVC-HelloWorld

一、SpringMVC简介 1.1 SpringMVC和三层架构 MVC是一种软件架构思想&#xff0c;将软件按照模型、视图和控制器三个部分划分。 M&#xff1a;model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;用于处理数据。JavaBean分为两类&#xff1a; 实体类Bean&…

KG+LLM(一)KnowGPT: Black-Box Knowledge Injection for Large Language Models

论文链接&#xff1a;2023.12-https://arxiv.org/pdf/2312.06185.pdf 1.Background & Motivation 目前生成式的语言模型&#xff0c;如ChatGPT等在通用领域获得了巨大的成功&#xff0c;但在专业领域&#xff0c;由于缺乏相关事实性知识&#xff0c;LLM往往会产生不准确的…

瑞吉外卖项目详细总结

文章目录 瑞吉外卖1.技术栈2.项目文件架构3.业务功能模块&#xff08;例子&#xff09;3.1管理员登录接口层(Controller)3.2管理员登录实现层(ServiceImpl)3.3管理员登录服务层&#xff08;Service&#xff09;3.4管理员登录Mapper层 4.公共模块4.1 BaseContext&#xff08;保存…

王力机器人安全门|用细节开拓高端精致家居生活

细微之处见风范,毫厘之优定乾坤。在追求高端品质的道路上,细节往往是最有力的诠释。如在入户门的选择方面,考虑到老人、孩子、宠物等每一位家庭成员不同需求的设计、科技运用才称得上是充满人性化、品质化的高端细节,幸福感直抵心灵。在该方面,王力机器人安全门做出了表率,每一…

【连接池】-从源码到适配(下),使用dynamic-datasource导致连接池没生效(升级版本)

写在前面 书接上文&#xff0c;连接池没生效&#xff0c;启用了一个什么默认的连接池。具体是什么&#xff0c;一起来看看源码吧。 目录 写在前面一、问题描述二、本地调试三、升级dynamic-datasource四、新的问题&#xff08;一&#xff09;数据源初始化问题&#xff08;二&am…

公司创建百度百科需要哪些内容?

一个公司或是一个品牌想要让自己更有身份&#xff0c;更有知名度&#xff0c;更有含金量&#xff0c;百度百科词条是必不可少的。通过百度百科展示公司的详细信息&#xff0c;有助于增强用户对公司的信任感&#xff0c;提高企业形象。通过百度百科展示公司的发展历程、领导团队…

ASP.Net实现汽车添加查询(三层架构,含照片)

演示功能&#xff1a; 点击启动生成页面 点击搜索模糊查询 点击添加跳转新界面 此处设置文本框多行 点击Button添加 步骤&#xff1a; 1、建文件 下图是三层架构列表&#xff0c;Models里面有模拟数据库中列的类&#xff0c;DAL中有DBHelper和service,BLL中有BllManager文件…

TiDB 7.5 LTS 发版丨提升规模化场景下关键应用的稳定性和成本的灵活性

互联网时代&#xff0c;数据的迅猛增长给数据库带来了可扩展性的挑战&#xff0c;Gen AI 带来的数据暴增更加剧了这种挑战。传统的数据分片已经不能承载新时代数据暴增的需求&#xff0c;更简单且具有前瞻性的方法则是采用原生分布式数据库来解决扩展性问题。在这种规模化场景的…

SpringValidation自定义注解以及分组校验

SpringValidation的参数校验使用可参考&#xff1a;【SpringMVC应用篇】Spring Validation 参数校验-CSDN博客 目录 1. 引入依赖 2. 自定义注解校验 2.1 创建Validation类 2.2 创建注解对象 2.3 使用注解 3. 分组校验 3.1 实体类内部定义接口 3.2 在参数上指定分组 1. …

CISSP 第1章:实现安全治理的原则和策略

作者&#xff1a;nothinghappend 链接&#xff1a;https://zhuanlan.zhihu.com/p/669881930 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 CIA CIA 三性&#xff1a; 机密性&#xff1a;和数据泄露有关。完整性…

存算分离降本增效,StarRocks 助力聚水潭 SaaS 业务服务化升级

作者&#xff1a;聚水潭数据研发负责人 溪竹 聚水潭是中国领先的 SaaS 软件服务商&#xff0c;核心产品是电商 ERP&#xff0c;协同350余家电商平台&#xff0c;为商家提供综合的信息化、数字化解决方案。公司是偏线下商家侧的 toB 服务商&#xff0c;员工人数超过3500&#xf…

C++初阶------------------入门C++

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

带大家做一个,易上手的家常糖醋白菜

准备 如果是大白菜就一个 小白菜就要两个 因为白菜炒完之后会变少 将白菜叶剥开每叶分成三个小块 整个剥完之后 放入盆中清洗干净 调一个糖醋汁 一勺料酒 两勺生抽 三勺白砂糖 四勺香醋 起锅烧油 放两个干辣椒 辣椒炒一下 然后倒入白菜 翻炒直到油全部融入白菜 然后倒入…

Windows本地如何部署Apache服务器搭配内网穿透实现无公网IP远程访问?

文章目录 前言1.Apache服务安装配置1.1 进入官网下载安装包1.2 Apache服务配置 2.安装cpolar内网穿透2.1 注册cpolar账号2.2 下载cpolar客户端 3. 获取远程桌面公网地址3.1 登录cpolar web ui管理界面3.2 创建公网地址 4. 固定公网地址 前言 Apache作为全球使用较高的Web服务器…

自然语言处理1——探索自然语言处理的基础 - Python入门篇

目录 写在开头1. 介绍自然语言处理的基本概念1.1 NLP的核心目标1.2 常见的NLP任务1.3 应用场景详细介绍1.3.1 医疗保健1.3.2 金融领域1.3.3 教育领域1.3.4 社交媒体分析 2. Python中常用的自然语言处理库简介2.1 NLTK (Natural Language Toolkit)2.2 Spacy2.3 Transformers2.4 …

张量操作与线性回归

一、张量的操作&#xff1a;拼接、切分、索引和变换 &#xff08;1&#xff09;张量拼接与切分 1.1 torch.cat() 功能&#xff1a;将张量按维度dim进行拼接 • tensors: 张量序列 • dim : 要拼接的维度 torch.cat(tensors, dim0, outNone)函数用于沿着指定维度dim将多个张量…

ES6之生成器(Generator)

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…

复兴计划01-lc06

StringBuilder和StringBuffer的区别 1. StringBuffer和StringBuilder都是用于字符串动态拼接,但是StringBuffer拼接的函数方法的实现中用了synchornized上锁&#xff0c;效率较低&#xff0c;不过可以用于多线程以此来维护线程安全&#xff1b;相比之下&#xff0c;StringBuil…

理解SQL中not in 与null值的真实含义

A not in B的原理是拿A表值与B表值做是否不等的比较, 也就是a ! b. 在sql中, null是缺失未知值而不是空值。 当你判断任意值a ! null时, 官方说, “You cannot use arithmetic comparison operators such as , <, or <> to test for NULL”, 任何与null值的对比都将返…

Postman使用

Postman使用 Pre-request Script 参考&#xff1a; Scripting in Postman 可以请求、集合或文件夹中添加Pre-request Script&#xff0c;在请求运行之前执行JavaScript 如设置变量值、参数、Header和正文数据&#xff0c;也可以使用Pre-request Script来调试代码&#xff0…