一些关于单链表的操作

news/2024/10/17 18:14:16/

思维导图:

一, 链表

1.1节点的结构

链表是啥样的啊?顾名思义链表就是一种用链子链接起来的表。那这种表是怎么样的啊?

这样的呗:

现在,我们知道了链表的形状了。那我们该如何用编程语言来形成这一种形状呢?

首先,一个链表节点是这样的:

毫无疑问,链表也需要存储一些数据(不然要链表来干嘛?)。而且链表还有一条尾巴(当然这是为了形象而画出来的)这条尾巴又要如何形成呢?现在让我们来想一想尾巴的作用是什么啊?当然是为了找到下一个节点啊,那我们要找到下一个节点的话我们应该用什么去找呢?当然是下一个节点的地址啊!那放地址我们是不是应该要用指针啊?那好,现在我们已经清楚的知道了这一个节点的结构组成了。画图如下:

现在,我们再往下推。首先,我们这一个结构里面含有两种不同类型的数据。不同类型的数据应该用什么数据结构来存储呢?明显是结构体。那我的指针指向的下一个节点是不是又是一个结构体或者空指针呢?那这个指针是不是又可以定义为结构体指针呢?答案是肯定的!

经过以上分析,现在我们就可以把代码写出来了:

代码:

typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;//这里的typedef是为了在后面对这个结构体更好的使用

 1.2节点的生成

现在我们已经把节点的结构搞明白了,我们就要进入第二阶段了。我们要把上面的结构的节点生成一下。生成节点用什么啊?malloc啊!

生成节点代码:

SListNode* BuySListNode(SLTDateType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));//动态开辟空间if (newnode == NULL) {printf("malloc fail\n");return NULL;}newnode->data = x;//在数据区内放入数据newnode->next = NULL;//先置空指针区return newnode;//将新节点返回
}

1.3打印链表

打印链表该怎么做呢?

其实很简单,这样做就行了!

代码:

void SListPrint(SListNode* plist) {SListNode* cur = plist;while (cur) {printf("%d->", cur->data);cur = cur->next;}printf("->NULL\n");//当链表为空的时候就打印一个NULL来提示一下
}

1.4在链表中插入数据

         现在我们的链表的设计已经写好了,但是这个链表是一个空链表。现在我们就要来给这个链表插入数据。当然,插入数据的方法有很多种。像头插,尾插,中间插入等等……。现在我们就来一 一实现一下。

1.4.1:头插

头插的方法就是生成一个新的节点,然后让新的节点成为新的头节点连接*pplist。然后再移动*pplist的位置让它指向新的节点。

void SListPushFront(SListNode** pplist, SLTDateType x) {SListNode* newhead = BuySListNode(x);newhead->next = *pplist;*pplist = newhead;	
}

其实,这里的头插逻辑基本上没有什么难度。但是这里使用一级指针还是二级指针才是需要花时间去理解的!可以看到我们这里使用的是二级指针,为什么呢?一句话——“你要改变谁就要用谁的指针”。因为在test文件里面我用的是结构体的指针:

SListNode* plist = NULL;

所以我要改变外面的plist的话就要用plist的指针了,因为指针的指针就是二级指针!所以我这里就用了二级指针!!!(解答完毕)

1.4.2:中间插入

先来讲一下我的中间插入的逻辑:我的插入是在我要找到的数据的后一个位置插入,所以找到位置很重要所以我会写一个查找数据位置的函数

代码;

SListNode* SListFind(SListNode* plist, SLTDateType x) {assert(plist);SListNode* pos = plist;while (pos) {if (pos->data == x) {return pos;//找到了就返回,结束循环}pos = pos->next;}printf("你要找的值不存在\n");return NULL;//找不到就返回空
}

写完了寻找节点的函数以后,我们就可以来写一个中间插入的函数来。代码如下:

void SListInsertAfter(SListNode* pos, SLTDateType x)
{assert(pos);//要对pos断言一下,因为这是中间插入,链表中有节点才能中间插入!SListNode* newnode = BuySListNode(x);SListNode* next = pos->next;//记录一下pos指向的下一个节点,因为pos->next要被改变!pos->next = newnode;newnode->next = next;}

1.4.3尾插

尾插?啥是尾插啊?顾名思义,尾插便是找到链表的尾巴然后再来插入,具体的逻辑可以参见代码。

void SListPushBack(SListNode** pplist, SLTDateType x)
{SListNode* newtail = BuySListNode(x);SListNode* tail = *pplist;if (tail == NULL) {//如果链表为空那就直接让*pplist指向newtail*pplist = newtail;}else {while (tail->next) {//这里找的是尾节点,所以当tail->next为空时就找到了尾节点了tail = tail->next;}tail->next = newtail;}
}

 1.5在链表中删除节点

和链表的插入一样,链表节点的删除也有三种。分别是头删,尾删,中间删除!

1.5.1:头删

头删,顾名思义便是删除头节点。思路很简单,就是调整一下plist的指向让plist指向下一个节点然后释放上一个节点就可以了。

代码:

void SListPopFront(SListNode** pplist) {assert(pplist);//pplist不可能为空,所以这里要预防一下assert(*pplist);//空链表不能删SListNode* del = *pplist;*pplist = (*pplist) ->next;free(del);del = NULL;
}

1.5.2:中间删除

这个操作的实现也是需要和 SListFind搭配使用的,前面我已经将 SListFind写好了。这里我就不再写了,我在这里直接写中间删除的函数就好了。当然这个中间删除函数与前面的中间插入一样,都只是删除中间的节点而不会删除头节点与尾节点。所以依照这个思路,我们便可以将代码写出来:

 代码:

void SListEraseAfter(SListNode* pos) {assert(pos);//中间删除不会删除头节点,更不会删除不存在的值。assert(pos->next);//也不能删除空指针SListNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}

1.5.3尾删

顾名思义,尾删就是把链表的尾节点删除掉。删除的逻辑就是找到链表的尾节点的前面一个节点然后将这一个节点中的指向改为空。并把尾节点给释放掉。

代码:

void SListPopBack(SListNode** pplist) {assert(pplist);//指针的地址不可能为NULLassert(*pplist);//空链表不能尾删if ((*pplist)->next == NULL) {free(*pplist);*pplist = NULL;}else {SListNode* tail = *pplist;while (tail->next->next) {tail = tail->next;}SListNode* del = tail->next;tail->next = NULL;free(del);del = NULL;}
}

1.6销毁链表

因为我们的链表是malloc出来的一块一块的空间,所以当我们的链表用完以后就要对这些一块一块的空间进行释放。说所以我们又要写一个函数来销毁链表。

代码:

void SListDestroy(SListNode** pplist) {assert(pplist);assert(*pplist);//链表未创建不能销毁SListNode* cur = NULL;while (*pplist) {cur = (*pplist)->next;free(*pplist);*pplist = cur;}*pplist = NULL;printf("链表销毁成功!\n");
}

总结:

到这里我们就已经把我能讲的一些关于链表的知识,我所知道的关于链表的知识给讲完了。但是我还是想要总结一下:

1.在我们要操作链表时,比如想要增删时就要用二级指针。

2.当一个变量不可能为空指针时要对它进行assert断言。

3.当一个变量为空时会导致错误时也要对这个变量进行assert断言。

好了,总结完毕!


http://www.ppmy.cn/news/55596.html

相关文章

倾斜摄影超大场景的三维模型轻量化纹理压缩的关键技术

倾斜摄影超大场景的三维模型轻量化纹理压缩的关键技术 倾斜摄影超大场景的三维模型轻量化处理中纹理压缩是轻量化处理的重要手段之一,可以在保证模型真实感的前提下,减小数据体积、降低传输带宽和提高渲染性能。以下是几个关键的纹理压缩技术&#xff1a…

边缘计算在哪些场景的应用?实现了哪些功能

边缘计算是一种分布式计算模型,将数据处理和存储功能从云中心移动到接近数据源的边缘设备上,从而在处理延迟、网络带宽、隐私保护和数据安全等方面带来了许多优势。 智慧油站应用:在加油区部署的吸烟检测、打电话检测、烟火检测、区域入侵检测…

配置Bridge模式KVM虚拟机

配置Bridge模式KVM虚拟机 1. 配置基本环境 1 安装软件包。 安装brctl和tunctl命令行工具,要采用Bridge模式的网络配置,首先需要安装两个RPM包,即bridge-utils和tunctl,它们提供所需的brctl和tunctl命令行工具。可以用yum工具安…

【go】channel结构体源码和读写和关闭过程

简而言之,channel维护了一个带指针的接受和发送的队列,其中包含mutex锁保证并发安全,数据类型,元素个数,元素大小,channel状态然后读写操作,先看队列是否可以取出,然后看缓冲区,最后…

基于Springboot的班级综合测评管理系统的设计与实现

摘要 随着互联网技术的高速发展,人们生活的各方面都受到互联网技术的影响。现在人们可以通过互联网技术就能实现不出家门就可以通过网络进行系统管理,交易等,而且过程简单、快捷。同样的,在人们的工作生活中,也就需要…

mybatis generator自定义model的代码注释

mbg相信大家都比较熟悉,可以自动化的生成数据库表对应的model,mapper。但是最近在使用mbg的时候遇到了这样的问题: 1、生成的model虽然可以根据数据库字段的comment生成注释,但这些注释仅对后端开发人员可见,如果想让前…

4 月份 火火火火 的开源项目

盘点 4 月份 GitHub 上 Star 攀升最多的开源项目,整个 4 月份最火项目 90% 都是 AI 项目(准确的说,最近半年的热榜都是 AI 项目) 本期推荐开源项目目录: 1. AI 生成逼真语音 2. 复旦大模型 MOSS! 3. 让画中…

23年4月工作笔记整理(前端)

目录 一、业务需求二、前端学习 一、业务需求 1.单个校验触发this.$refs[‘表单ref’].validateField(‘单个校验名’) 2.return 只会退出当前循环,不是退出方法,与break类似 3.store里的数据刷新会消失,可以采取重新调接口,或者…