【数据结构】——线性表之单链表

一、单链表的概念和结构

1、单链表的概念:

链表也是属于我们的线性表中的一种,其物理结构上是不一定连续的,但是逻辑结构上是一定连续的,所以其是没办法像前面的顺序表一样通过++找到下一个元素的,其是通过指针来找到下一个元素的。

好比如,我们平时坐的火车,我们会发现火车也是有长有短的,当平时人少的时候,我们火车的车厢数量就会少一点,然后到节假日的时候,那么此时的火车车厢数量就会增加,火车每个车厢处是会有一个节点来链接的,然后我们要想走到后面的车厢 ,是必须要经过前面的车厢才可以。

如下所示:

我们的链表其实大致也是这样,其由两个部分组成,一个是要存储的数据,还有一个是指向下一个节点的指针。

如下所示:

 2、单链表的节点:

和前面学习的顺序表不太一样的是,我们的链表中的每个节点都是单独申请空间的,节点中主要由要存储的数据和指向下一个节点的指针构成,这样我们就可以通过这个指针来找到下一个节点的数据了,但不能从后一个节点找到前节点的。

还有就是这个指针变量,并不是要存储的数据类型的指针变量,因为我们要这个指针指向的是下一个节点,那么其类型就应该是节点类型。

下面我们看看单链表的节点是如何进行定义的:

那么我们上面定义的节点中,其是用来存放整型数据的,所以第一个成语是要存储的数据,然后第二个成员要指向下一个节点的指针,那么有的同学可能会认为是int*,因为我们存储的数据类型是int类型的,但是我们别忘记了,我们指向的是节点,节点中不仅有这个存储的数据,还有指向下一节点的指针,所以其类型有应该是结构体指针,也就是节点类型指针即:struct  SListNode*类型的指针。

然后因为我们对于存放的数据可能会有不同,那么这里我们使用typedef关键字定义一个类型,到后面我们要是需要其他类型的直接对这个定义进行修改即可。

3、单链表的性质: 

  • 链表是无序的结构,其在逻辑结构上是连续的,在物理结构上是不一定连续的。
  • 节点一般是一个一个申请的空间,不是一块进行申请的,其在堆上进行申请的,我们使用malloc函数进行申请。
  • 我们在堆上申请的空间是不知道连续不连续的,所以节点在内存中是不一定连续的
  • 要访问链表中的元素时,我们只需要知道链表的头节点即可,根据节点中存储下一节点的指针变量可以找到下一节点的数据。所以我们一般会专门使用一个指针变量来存储链表的头节点。

二、实现单链表 

1、定义一个单链表

下面我们先是实现对一个单链表的创建,这次我们创建一个头文件SList.h头文件来定义好这个单链表的结构。

 2、单链表的节点申请

我们链表的申请和顺序表的申请是有很大的不同的,首先我们顺序表中是直接申请一块空间,我们的链表是需要存储数据的时候再进行申请对应大小的空间,所以我们的节点申请的时候,是需要将要存储的数据进行输入的,所以函数的参数应该为要存放的数据的类型,然后我们进行空间的申请,申请的空间的大小应该为一个节点的大小,然后我们使用一个指针变量来接收,然后再使用if语句进行判断其空间是否申请成功,如果不成功就使用perror来判断其内没有申请成功的原因。

然后我们使用就将这个要存储的数据存储进这个节点,然后将这个节点指向下一个节点的指针置为NULL。

然后再将这个节点返回即可。

3、单链表数据的访问

我们的访问主要是想将链表的内容打印到屏幕上,看看链表的情况是否按照我们想要的方向去走。

前面我们对于顺序表的打印,主要是通过++进行,这是因为顺序表在物理结构上是连续的,其是挨着存放的。现在我们对于单链表,我们在定义的时候有讲到,我们可以通过当前的节点找到下一节点,我们就可以从这个链表的头节点开始往后找,就可以找到所有的数据元素。

我们函数名字取为:SLTPrint,然后我们需要从头节点开始找,所以我们要传入的参数也应该为一个指针,那么我们可以创建一个指针变量,用来存储我们的头节点,然后使用一个循环,只要这个指针变量不为空,那么就可以打印数据,但是还有个问题就是我们这个指针变量是指向头节点的,然后我们要是对其进行赋值下一节点的地址,由于我们是地址传递的,那么其形参的改变是会造成实参的改变的,那么我们就可以在函数体内部创建一个节点类型的指针变量来替代这个头节点进行下一节点的寻找。

然后当其为NULL的时候,就说明我们的链表已经打印完成,然后这个临时的指针变量不用理,当这个函数执行完后,编辑器会自己释放的。

函数实现如下:

我们来链表打印完后,再进行打印一个NULL表示当前的链表数据已经完全打印出来,或者当当前链表为空表的话,那么就表示当前链表是个NULL的表。

4、 链表的头插和尾插

头插:

在对于链表的创建中,我们就是使用一个指针变量phead节点指针来指向这个链表的头节点, 最开始对其置为NULL,而且不需要进行初始化操作,每个节点在创建的时候就已经输入了数据了。

头插就是将这个节点变成这个链表的头节点,那么指向头节点的指针变量就变成指向这个要插入的节点,然后我们插入的这个节点,其成语中指向原来的头节点。·

那么细心的同学就会发现了,我们这里要改变的值,是地址,是一个指向节点的地址,是一个一级指针,那么我们在传参数的时候,就需要使用到二级指针了,这样才可以达到形参的改变会影响实参。

然后因为其传入的参数是一个指针,那么我们在函数的开头也是需要对其进行一个断言。

函数实现如下:

 我们可以看到链表的头插的时间复杂度为O(1)。

尾插:

尾插就是在链表的尾部进行插入数据,那么我们就需要找到链表的尾部才可以进行插入,其是尾插也很好理解,就是让当前链表的最后一个节点的指向要插入的节点的指针,然后要插入的这个节点指向下一节点的指针为NULL即可。

但是我们也要考虑到一个特殊情况,那么就是当前的链表是一个空链表的情况,那么此时我们的链表就不存在尾节点,那么就是我们当前要插入的节点变成这个链表的头节点。

那么对于这种情况,我们直接让这个指向头节点的指针指向这个要插入的节点,那么我们可以使用一个if语句。

然后如果不是空链表,那么我们创建一个指针变量来代替保存着头节点的指针变量往后找到链表的尾节点,然后进行尾插。

那么我们使用一个while循环往后找,直到我们这个指针当前的位置在尾节点的时候,也就是这个节点的next指针变量为NULL的时候结束往下找,然后对其进行修改,使其指向要插入的节点,然后要插入的节点的next指针要为NULL。

函数如下:

5、链表的头删和尾删 

头删:

头删就是将当前的头节点从链表中删除,那么其该如何进行删除呢?

那么我们就需要将头节点的这个空间进行释放,还给操作系统,然后第二个节点就顺位成为头节点,那么我们也需要找到第二个节点。那么我们需要先找到第二个节点才行,那么我们先创建一个指针变量来存储当前头节点的地址,然后将指向头节点的指针指向下一节点。然后再对这个空间进行释放。 然后我们是要对指向头节点的指针进行修改,那么我们要使用二级指针来接收。然后就是我们不能传一个空表进来,空表的话没有东西可以进行删除。那么我们对指向头节点的指针进行断言。

函数实现如下:

尾删:

尾删很好理解的,前面我们已经写了尾插了,其也是一样,首先我们要找到尾节点,然后才可以对其进行释放,那么我们就需要进行遍历,找到尾节点,那么我们创建一个指针变量来进行遍历,对其赋值为头节点的地址。然后找到尾节点后,我们对其进行内存释放,然后其前一个节点就成为了尾节点了,那么其指向下一个指针的指针变量也要指向NULL,那么我们再

是不是这样呢?我们下面写一下看看:

当我们的链表只有一个节点的时候呢?那么此时的链表其首节点也是尾节点,此时尾节点的前面是没有节点的,那么我们前面要找其前一个节点,对其是不成立的。那么我们可以分情况进行操作,对于其只有一个节点的情况就特殊处理。

正确代码:

 

6、查找指定节点 

查找指定节点,不用进行啥操作,就是进行查询,那么我们很容易就想到的遍历,然后看看能不能找到想要的数据即可。要是找到,就返回其对于的节点,如果没有找到那么就返回一个空。那么我们要创建一个指针变量来进行遍历。

然后这个功能是不需要对实参进行修改的,那么我们这里的参数就不需要使用到二级指针了。

7、指定位置删除和插入 

指定位置的删除和插入,我们要搭配上面的查找指定节点的函数进行使用,我们使用查找指定节点的函数找到我们要插入的位置,或者删除的位置,然后进行操作。

删除指定节点:

删除链表中指定位置的节点,我们首先使用上面的查找函数进行查找,然后使用一个节点类型指针来接收这个地址,然后我们要删除这个节点,那么这个节点前面的节点吗,其指向下一个节点的指针就需要指向这个要删除的节点的下一个节点。然后我们再看两个特殊的位置,就是尾节点和头节点,尾节点的话,我们删除后,其前一个节点指向的确实NULL。

那么我们再看看头节点,我们前面提到要将这个要删除的节点的前节点指向下一个节点,但是我们的头节点是没有前节点的,那么这个是矛盾的,那么我们就可以特殊处理一下。然后我们前面写了一个头删,所以这个情况我们直接使用头删除即可。

在指定位置之前插入: 

我们要在指定节点前插入数据,那么我们就需要得到指定位置前的地址,但是我们的链表是没办法直接得到前一个节点的地址的,那么我们就可以通过头节点来找到,即可这个节点的下一节点为pos的时候就找到了,然后我们让这个节点的指针变量指向要插入的节点,然后这个插入的节点指向指定的这个位置。

但是我们和上面一样,这个指定位置前插入数据要有前节点才可以,那么我们要是指定的位置是头节点前,那么这个逻辑就矛盾了,那么我们就特殊处理这个情况即可,当这个指定的节点是头节点的时候,那么就是我们的头插了,那么我们调用头插函数即可。

函数实现如下:

 在指定位置之后插入数据:

在指定的位置之后插入数据,那么我们就将这个指定的位置指向下一个节点的指针指向这个要插入的节点,然后这个插入的节点要指向这个指定位置的后一个节点,那么我们的顺序是如何呢?

我们应该先让这个要插入的节点指向下一个节点的指针指向这个指定位置的下一个节点,这是因为我们要是先使这个指定位置指向下一节点的指针指向了这个要插入的节点,那么我们要找这个指定位置的一下节点就找不到了。

然后对于尾节点这个特殊位置,我们尾节点后面的节点为空,这个直接插入是可以的。

8、链表的销毁

在上面我们实现了一系列对于链表的操作,那么我们使用完这个链表后,就得将这个链表进行销毁吧,提高空间的利用效率。那么我们的链表是如何进行销毁的呢?前面我们的顺序表,是可以直接使用一个free进行释放,这是因为其物理结构是连续的,其是在内存中使用的是一块连续的空间的。

但是我们的链表并不是,其是每个节点独立进行空间的申请的,其物理结构是不连续的,所以我们的链表的销毁,就需要直接进行遍历,然后对其进行释放,我们释放好一个后,就继续下一个节点的释放,直到释放好全部节点,那么我们在释放节点前,先将其指向下一个节点的指针变量指向的地址使用一个节点类型指针来存放,这样才可防止当前的节点释放后能够顺序找到下一节点。

 在销毁完后,不要忘记给指向头节点的指针变量置空,防止其成为野指针。

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

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

相关文章

线程函数库

pthread_create函数 pthread_create 是 POSIX 线程库&#xff08;pthread&#xff09;中的一个函数&#xff0c;用于创建一个新的线程。 头文件 #include <pthread.h> 函数原型 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*s…

2.5 桥梁桥面系及附属结构施工

2.5.1 桥面系施工 1.排水设施 设置纵横坡及泄水孔&#xff0c;减少桥面积水、防排结合。汇水槽、泄水孔顶面高程低于桥面铺装10-15mm。泄水孔边缘设渗水盲沟泄水管下端至少应伸出构筑物底面100-150mm。泄水管通过竖向管道直接引至地面或雨水管线。竖向管道抱箍、卡环、定位卡…

docker 代理配置冲突问题

问题描述 执行 systemctl show --property=Environment docker 命令看到有如下代理配置 sudo systemctl show --property=Environment docker Environment=HTTP_PROXY=http://127.0.0.1:65001 HTTPS_PROXY=http://127.0.0.1:65001 NO_PROXY=127.0.0.1,docker.io,ghcr.io,uhub…

MATLAB基础应用精讲-【基础知识篇】发布和共享 MATLAB 代码

目录 MATLAB发布代码---生成文档pdf 分节符对发布文件的分节 实时脚本 Matlab workspace与m脚本数据共享 发布和共享 MATLAB 代码 在实时编辑器中创建和共享实时脚本 发布 MATLAB 代码文件 (.m) 添加帮助和创建文档 发布 MATLAB 代码文件 (.m) 可创建包括您的代码、注释…

JDBC 批处理与事务处理:提升数据操作效率与一致性的密钥

目录 一. JDBC批量添加数据 1. 什么是批量添加数据 2. 实现数据的批量添加 a. 方式一&#xff1a;不分块 二. JDBC事务处理 1. 什么是事务 2. JDBC事务处理实现 三. 总结 前言 本文来讲解JDBC的批处理和事务处理 这对数据的安全性和准确性以及高效率提供很好的办法 话不…

C++实现Atbash密码

详细说明 埃特巴什密码是一种替换密码&#xff0c;在该密码中字母表中的字母是反向对应的。例如&#xff0c;A 会被替换为 Z&#xff0c;B 会被替换为 Y&#xff0c;依此类推。 #include <cassert> /// for assert #include <iostream> /// for IO operations #…

QuecPython+GNSS:实现快速定位

概述 QuecPython 结合 GNSS&#xff08;全球导航卫星系统&#xff09;模块为物联网设备提供开箱即用的定位能力解决方案。该方案支持 GPS/北斗/GLONASS/Galileo 多系统联合定位&#xff0c;为物联网开发者提供从硬件接入到云端服务的全栈式定位解决方案。 优势特点 多体系定…

leetcode刷题日记——逆波兰表达式求值

[ 题目描述 ]&#xff1a; [ 思路 ]&#xff1a; 借助栈的特性&#xff0c;遇见数字就将这个数压入栈内&#xff0c;遇见符号&#xff0c;就从栈中弹出两个数&#xff0c;进行相应的运算&#xff0c;然后将结果压入栈中运行如下 int evalRPN(char** tokens, int tokensSize…

firewalld 详解

firewalld 详解 firewalld 是 Linux 系统中一个动态防火墙管理工具&#xff0c;取代了传统的 iptables&#xff0c;提供更灵活、动态的规则配置&#xff0c;支持运行时修改且无需重载服务。以下是其核心概念、常用操作及示例指南&#xff1a; 一、核心概念 区域&#xff08;Zo…

面向高性能运动控制的MCU:架构创新、算法优化与应用分析

摘要&#xff1a;现代工业自动化、汽车电子以及商业航天等领域对运动控制MCU的性能要求不断提升。本文以国科安芯的MCU芯片AS32A601为例&#xff0c;从架构创新、算法优化到实际应用案例&#xff0c;全方位展示其在高性能运动控制领域的优势与潜力。该MCU以32位RISC-V指令集为基…

支付宝小程序组件与页面构造器使用指南:从页面到组件的正确迁移

引言 在支付宝小程序开发中&#xff0c;我们经常会遇到需要将页面组件化的情况。本文将通过一个实际案例&#xff08;将 /pages/plugin/device 从页面迁移到组件&#xff09;&#xff0c;深入分析支付宝小程序中页面和组件的区别&#xff0c;以及正确的迁移方式。我们将从问题…

26-算法打卡-字符串-右旋字符串-第二十六天

1 题目说明 字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k&#xff0c;请编写一个函数&#xff0c;将字符串中的后面 k 个字符移到字符串的前面&#xff0c;实现字符串的右旋转操作。 例如&#xff0c;对于输入字符串 &qu…

fastbev mmdetection3D 角度和方向损失

角度/方向损失 sin(a−b)sinacosb−cosasinb config参数 dir_offset0.7854, # pi/4 dir_limit_offset0, box编解码 # Copyright (c) OpenMMLab. All rights reserved. import torchfrom mmdet.core.bbox import BaseBBoxCoder from mmdet.core.bbox.builder import BBOX_COD…

极狐GitLab 如何 cherry-pick 变更?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 拣选(cherry-pick)更改 (BASIC ALL) 在 Git 中&#xff0c;cherry-pick 是从一个分支获取一个提交并将其添加为另一个分支的…

java多线程(7.0)

目录 ​编辑 定时器 定时器的使用 三.定时器的实现 MyTimer 3.1 分析思路 1. 创建执行任务的类。 2. 管理任务 3. 执行任务 3.2 线程安全问题 定时器 定时器是软件开发中的一个重要组件. 类似于一个 "闹钟". 达到一个设定的时间之后, 就执行某个指定好的…

优化非线性复杂系统的参数

非线性项组合的系统 对于系统中的每一个复杂拟合&#xff0c;即每一个残差函数&#xff0c;都能表示为非线性方程的趋势&#xff0c;例如较为复杂的系统函数组&#xff0c; from optimtool.base import sp, np x sp.symbols("x1:5") res1 0.5*x[0] 0.2*x[1] 1.…

清华LeapLab开源Cooragent框架:一句话构建本地智能体服务群,让AGI真正触手可及

引言&#xff1a;智能体革命&#xff0c;从复杂到简单 在人工智能发展的浪潮中&#xff0c;Agent&#xff08;智能体&#xff09; 技术被视为实现通用人工智能&#xff08;AGI&#xff09;的关键路径。然而&#xff0c;传统智能体的开发与协作始终面临两大痛点&#xff1a;依赖…

云原生--核心组件-容器篇-1-Docker和云原生关系(Docker是云原生的基石)

1、基本概念 &#xff08;1&#xff09;、云原生&#xff08;Cloud Native&#xff09; 是一种构建和运行应用程序的方法论&#xff0c;旨在充分利用云计算环境&#xff08;公有云、私有云、混合云&#xff09;的特性&#xff0c;通过容器化、微服务、服务网格、声明式API等技…

问答页面支持拖拽和复制粘贴文件,MaxKB企业级AI助手v1.10.6 LTS版本发布

2025年4月24日&#xff0c;MaxKB开源企业级AI助手正式发布v1.10.6 LTS版本。这一版本主要进行了一些功能优化和问题修复。 功能优化 ■ 应用&#xff1a;文件上传支持上传其他自定义的文件类型&#xff0c;该类型文件需要自行写入函数解析&#xff1b; ■ 问答页面&#xff…

用户案例--慧眼科技

作者&#xff1a;算力魔方创始人/英特尔创新大使刘力 每个行业都有其独特的需求&#xff0c;算力魔方推出了全面的定制化服务&#xff0c;从概念到产品化&#xff0c;满足各行各业&#xff0c;用户可以根据具体应用需求定制更多接口或更强图形处理的需求&#xff0c;且算力魔方…