C语言菜鸟入门·数据结构·链表超详细解析

 

目录

1.  单链表

1.1  什么是单链表

1.1.1  不带头节点的单链表

1.1.2  带头结点的单链表

1.2  单链表的插入

1.2.1  按位序插入

(1)带头结点

(2)不带头结点

1.2.2  指定结点的后插操作

1.2.3  指定结点的前插操作

1.3  单链表的删除

1.3.1  按位序删除

1.3.2  指定结点的删除


1.  单链表

1.1  什么是单链表

        对比于顺序表的每个节点只存放数据元素,单链表的每个节点除了存放数据元素外,还要存储指向下一个节点的指针。

顺序表:

优点:可随机存储,存储密度较高;

缺点:要求大片连续空间,改变容量不方便。

单链表:

优点:不要求大片连续空间,改变容量方便;

缺点:不可随机存取,要耗费一定空间存放指针。

        对于单链表的每一个结点,都需要有一个数据域用于存放节点的数据元素,需要一个指针域用于指向下一个结点。

struct LNode{ElemType data;struct LNode *next;
};

对于struct关键字的用法可参考:C语言菜鸟入门·结构体·struct用法超详细解析_c语言数据结构菜鸟-CSDN博客

        而若是我们想要增加一个新的结点,我们可以使用malloc关键字,例如:

        首先,我们先声明一个指向struct LNode类型的对象p,通过malloc函数动态分配内存大小为struct LNode类型大小的内存。

struct LNode* p=(struct LNode*)malloc(sizeof(struct LNode));

        对于每次增加一个新的结点,我们每次都需要加上struct LNode这样声明起来有些太麻烦了。那么要怎么简单点呢?我们可以使用typedef进行重命名操作,那么就可以写为:

struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;

其中对于typedef的操作可以参考:C语言菜鸟入门·各种typedef用法超详细解析-CSDN博客中的结构体介绍。

1.1.1  不带头节点的单链表

        我们使用typedef后,可以开始创建一个不带头节点的单链表:

struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;//初始化一个空链表
bool InitList(LinkList &L)
{L = NULL;  //空表,按时还没任何结点,防止脏数据return trun;
}void test()
{Linklist L;//声明一个指向单链表的指针,注意此处没有创建一个结点//初始化一个空表InitList(L);//····后续代码····
}

        其作用是判断单链表是否为空,通过头指针L,初始化一个空表,防止脏数据:

        其中初始化一个空链表的代码也可以写为:

bool Empty(LinkList L)
{if(L==NULL)return true;elsereturn true;        
}

        或者:

bool Empty(LinkList L)
{return (L==NULL);      
}

1.1.2  带头结点的单链表

struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;//初始化一个空链表
bool InitList(LinkList &L)
{L = (LNode*)malloc(sizeof(LNode));//分配一个头节点if(L == NULL)//内存不足,分配失败return false;L->next = NULL;//头结点之后暂时还没有结点return true;}void test()
{Linklist L;//声明一个指向单链表的指针//初始化一个空表InitList(L);//····后续代码····
}

        不带头结点的头指针指向的下一个结点,这个结点就是实际用于存放数据的结点,

        带头节点的头指针指向的下一个结点,这个结点不用来存放实际的数据元素的,只有这个结点的下一个结点才会用来存放数据。

1.2  单链表的插入

1.2.1  按位序插入

(1)带头结点

        插入操作,例如在表L中的第i个位置上插入指定元素e:

Listlnsrrt(&L,i,e);

        我们要如何来实现插入操作呢?我们要想在i个位置上插入,那么我们就需要找到第i-1个结点,将新的节点插入其后,假设我们的i=2的话(a2),那么我们就需要找到其前一个结点(a1)的结点,然后我们使用malloc函数,申请一个新的结点,往这个结点存入指针e,然后修改指针,就可以得到:

struct LNode {ElemType data;struct LNode* next;
}LNode, * LinkList;//在第i个位置上插入元素e(带头结点)
bool ListInsert(LinkList& L, int i, ElemType e)
{if (i<1)return false;LNode* p;//指针p指向当前扫描到的结点int j = 0;//指针p指向第几个结点p = L;//L指向头结点,头结点是第0个结点(不存数据)while (p != NULL && j < i - 1)//循环找到第i-1个结点{p = p->next;j++;}if(p==NULL)//i值不合法return false;LNode* s = (LNode*)malloc(sizeof(LNode));s->date = e;s -> next = p->next;//将结点s连接到p之后p -> next = s;//插入成功return true;}

    s->date = e;s -> next = p->next;//将结点s连接到p之后p -> next = s;//插入成功

(2)不带头结点

        由于不带头结点,所以不存在头结点是0的情况,那么数据处理为:

struct LNode {ElemType data;struct LNode* next;
}LNode, * LinkList;//在第i个位置上插入元素e(带头结点)
bool ListInsert(LinkList& L, int i, ElemType e)
{if (i<1)return false;if (i == 1)//插入第1个节点的操作与其他结点操作不同{LNode* s = (LNode*)malloc(sizeof(LNode));s->date = e;s - next = L;L = s;return true;}LNode* p;//指针p指向当前扫描到的结点int j = 1;//指针p指向第几个结点p = L;//L指向头结点,头结点是第0个结点(不存数据)while (p != NULL && j < i - 1)//循环找到第i-1个结点{p = p->next;j++;}if(p==NULL)//i值不合法return false;LNode* s = (LNode*)malloc(sizeof(LNode));s->date = e;s -> next = p->next;//将结点s连接到p之后p -> next = s;//插入成功return true;}

1.2.2  指定结点的后插操作

        后插操作比较简单,对比按位序插入,仅仅将其改为指定插入,就明确告诉你我要插哪里:

struct LNode {ElemType data;struct LNode* next;
}LNode, * LinkList;bool InsertNextNode(LinkList* p, ElemType e)
{if(p==NULL)return false;LNode* s = (LNode*)malloc(sizeof(LNode));if(s==NULL)return false;s->date = e;s -> next = p->next;p -> next = s;return true;
}

1.2.3  指定结点的前插操作

        在链表中,我们并不能通过后一节点的数据data找到,前一个结点的指针域next,那我们要如何实现前插操作呢?

例如:我们想在结点1之前插入一个结点:

        首先,我们先创建一个结点:

         然后,我们既然找不到前驱结点,干脆不找了,直接将结点1的数据data1以及指针域next1复制到新建的结点,这样复制的结点数据就和结点1的数据相同:

        然后我们在将想要插入的结点赋值给结点1:

        然后将插入结点的next指向复制的结点1的data,让复制的结点1的next指向结点2的data,此时的复制结点1和结点1是相同的,结点插在复制的结点1之前,等价于结点插,插入到结点1之前:

struct LNode {ElemType data;struct LNode* next;
}LNode, * LinkList;bool InsertNextNode(LinkList* p, ElemType e)
{if(p==NULL)return false;LNode* s = (LNode*)malloc(sizeof(LNode));if(s==NULL)return false;s -> next = p->next;p -> next = s;s->date = p->data;p->data = e;return true;
}

1.3  单链表的删除

1.3.1  按位序删除

        删除操作,删除表L中的第i个位置的元素,并用e返回删除元素的值:

ListDelete(&L,i,&e);

        简单来说就是找到第i-1个结点,将其指针指向第i+1个结点,并释放第i个结点。

struct LNode {ElemType data;struct LNode* next;
}LNode, * LinkList;bool ListDelete(LinkList& L, int i, ElemType &e)
{if (i<1)return false;LNode* p;//指针p指向当前扫描到的结点int j = 0;//指针p指向第几个结点p = L;//L指向头结点,头结点是第0个结点(不存数据)while (p != NULL && j < i - 1)//循环找到第i-1个结点{p = p->next;j++;}if(p==NULL)//i值不合法return false;if (p->next == NULL)//第i-1个结点之后已无其他结点return false;LNode* q = p->next;//令q指向被删除的结点e = q->date;//用e返回元素的值p - next = q->next;//将*q结点从链中“断开”free(q);//释放结点的存储空间return true;}

1.3.2  指定结点的删除

        指定结点的删除,删除结点p,需要修改其前驱节点的next指针。有两种方法:

方法1:传入头指针,循环寻找p的前驱结点;

方法2:类似于结点前插的实现方式。

bool DeleteNode(LNode *p)
{if(p==NULL)//i值不合法return false;LNode* q = p->next;//令q指向*p的后续结点p->date = p -> next->date;//和后续结点交换数据域p -> next = q->next;//将*q结点从链中“断开”free(q);//释放结点的存储空间return true;
}

        但是以上代码,若是p是最后一个节点,那么代码:

    p->date = p -> next->date;//和后续结点交换数据域

        就会发生错误,解决方法:我们就只能从表头开始一次寻找p的前驱。

C语言_时光の尘的博客-CSDN博客

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

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

相关文章

如何对人工智能系统进行测试|要点,方法及流程

当今社会&#xff0c;人工智能发展非常快。现在人工智能的发展已经渗透到了我们生活的方方面面&#xff0c;自动驾驶、或者我们手机里经常用到的一些应用都或多或少涉及到了一些人工智能的功能&#xff0c;比如说美图秀秀、新闻推荐、机器翻译以及个性化的购物推荐等等都涉及到…

视频监控汇聚平台LntonCVS视频监控管理平台解决方案和常见的接入方式

一、视频融合平台 LntonCVS是一款支持多种协议和设备接入的视频汇聚流媒体平台。它能够统一管理和整合不同品牌、不同协议的视频资源&#xff0c;构建视频数据资源池&#xff0c;并通过视频资源目录为各类业务场景提供丰富、实时、高清的视频资源。 二、接入方式 1. 前端设备…

视频汇聚平台EasyCVR接入移动执法记录仪,视频无法播放且报错500是什么原因?

GB28181国标视频汇聚平台EasyCVR视频管理系统以其强大的拓展性、灵活的部署方式、高性能的视频能力和智能化的分析能力&#xff0c;为各行各业的视频监控需求提供了优秀的解决方案。视频智能分析平台EasyCVR支持多协议接入&#xff0c;兼容多类型的设备&#xff0c;包括IPC、NV…

【unittest】TestSuite搭建测试用例示例二

1.1 打开串口示例 常用的模组则包含AT指令测试&#xff0c;或串口数据测试&#xff0c;则可添加串口配置&#xff0c;将指令通过串口发送出去&#xff0c;如下所示&#xff1a; import serial def open_serial_port(port, baudrate115200, timeout2): try: # 创建并配置串…

Cocos Creator2D游戏开发(10)-飞机大战(8)-计分和结束

现在游戏基本能完了, 飞机能发射子弹,打了敌机,敌机也能炸; 接下来要做计分了; 步骤: 搞出一个lable让lable显示炸了多少飞机 开搞: ①创建一个Lable标签 ② root.ts文件 添加 property(Label) player_score: Label; // 标签属性 标签绑定 ③ 代码添加 注册 然后回调 contac…

计算机网络-数据链路层

基本概念 数据链路和链路 链路&#xff1a;指的是从一个节点到相邻节点的一段物理线路&#xff0c;且中间没有任何其他的交换节点 数据链路&#xff1a;传输数据时&#xff0c;除了一条物理线路&#xff0c;还需要一些必要通信协议来控制这些传输。 数据链路层的三个基本问…

【架构】客户端优化

这篇文章总结一下服务器网关及之前部分的优化&#xff0c;如客户端的优化&#xff0c;CDN/DNS等。 这里我们先谈一谈客户端缓存优化的手段。一般我们后端在说到缓存&#xff0c;第一时间想到的往往是redis&#xff0c;其实缓存在架构层次还有很多其他可以实现的地方&#xff0…

度言软件介绍

度言软件管理员操作后台 https://www.duyansoft.com企业后台为公司管理员操作后台&#xff0c;共计有七个功能版块 控制台 成员管理——员工管理 成员管理——员工管理&#xff08;添加员工&#xff09; 成员管理——团队管理 公司管理员可以新建/编辑/删除团队&#xff0c…

SSM整合快速学习

目录 步骤&#xff1a; 一、环境搭建 1.创建JdbcConfig配置类 2.创建JdbcConfig配置类 3.创建MybatisConfig配置类 4.创建jdbc.properties 5.创建SpringMVC配置类 6.创建Web项目入口配置类 二、功能模块开发 步骤1:创建数据库及表 步骤2:编写模型类 步骤3:编写Dao接…

Java面试题--JVM大厂篇之Java中Parallel GC的调优技巧与最佳实践

目录 引言&#xff1a; 正文&#xff1a; 1. 理解Parallel GC的工作原理 2. 常见痛点与解决方案 痛点一&#xff1a;长时间暂停 痛点二&#xff1a;频繁的Minor GC 痛点三&#xff1a;内存溢出 3. 调优参数推荐 4. 实战经验分享 结束语&#xff1a; 引言&#xff1a;…

定时任务-xxl-job

一. 为什么定时任务可以定时执行 定时任务可以定时执行的原理是通过操作系统提供的定时器实现的。 以下是定时任务能够准时执行的基本原理和相关技术&#xff1a; 操作系统的调度器&#xff1a; 操作系统&#xff08;如Linux、Windows等&#xff09;内部都有一个调度器&#x…

electron 配置、打包 -报错解决

目录 一、配置途中遇到的问题&#xff1a; 二、 make 配置好后开始打包 三、Electron-builder 打包报错 一、配置途中遇到的问题&#xff1a; 1. 安装 yarn add electron -D 一直卡在这里失败 一直卡可以使用下面这个&#xff0c;然后再重新装依赖 1. 采用新的镜像地址 npm …

机械学习—零基础学习日志(高数22——泰勒公式理解深化)

核心思想&#xff1a;函数逼近 在泰勒的年代&#xff0c;如果想算出e的0.001次方&#xff0c;这是很难计算的。那为了能计算这样的数字&#xff0c;可以尝试逼近的思想。 但是函数又不能所有地方都相等&#xff0c;那退而求其次&#xff0c;只要在一个极小的范围&#xff0c;…

Modbus-RTU详解

目录 Modbus-RTU协议 帧结构示例 CRC16校验算法 CRC16算法的过程 modbus-rtu的使用 发送数据 接收数据 tcp网口完整实现modbus-rtu协议 使用NModbus4实现modbus-rtu协议 安装NModbus4库。 串口实现NModbus4 Modbus-RTU协议 Modbus RTU 协议是一种开放的串行协议&#xff0c;广…

GroupMamba实战:使用GroupMamba实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

26集 ESP32 AIchat启动代码分析-《MCU嵌入式AI开发笔记》

26集 ESP32 AIchat启动代码分析-《MCU嵌入式AI开发笔记》 这集我们分析代码如何组织起来&#xff0c;如何编译 先用sourceinsight把代码加进工程。 新建一个sourceinsight工程&#xff0c;把AI-CHAT代码加进来&#xff0c;之后把ESP IDF代码加进来&#xff0c;之后把ESP-ADF加…

大语言模型(LLM)文本预处理实战

大语言模型&#xff08;LLM&#xff09;文本预处理实战 文章目录 大语言模型&#xff08;LLM&#xff09;文本预处理实战2.1 理解词嵌入2.2 文本分词2.3 将 token 转换为 token ID2.4 添加特殊上下文 token2.5 字节对编码 (BytePair Encoding, BPE)2.6 使用滑动窗口进行数据采样…

sql注入部分总结和复现

一个端口对应一个服务 联合查询注入 所有的程序中&#xff0c;单双引号必须成对出现 需要从这个引号里面逃出来 在后面查询内容 ?id1 要查库名&#xff0c;表名&#xff0c;列名。但是联合查询要知道有多少列&#xff0c;所以通过order by 去查询 order by # 通过二分法…

Cartopy简介和安装

Cartopy 是一个开源免费的第三方 Python 扩展包&#xff0c;由英国气象办公室的科学家们开发&#xff0c;支持 Python 2.7 和 Python 3&#xff0c;致力于使用最简单直观的方式生成地图&#xff0c;并提供对 matplotlib 友好的协作接口。初学Cartopy&#xff0c;欢迎指正&#…

算法回忆录(3)

11. 假设有7个物品&#xff0c;它们的重量和价值如下表所示。若这些物品均不能被分割&#xff0c;且背包容量M&#xff1d;150&#xff0c;设计算法求解怎么装才能使得获取的价值最大&#xff1f;请写出伪代码。 #include <stdio.h>#define MAX_ITEMS 100 #define …