你真的会数据结构吗:单链表

❀❀❀ 文章由@不准备秃的大伟原创 ❀❀❀

♪♪♪ 若有转载,请联系博主哦~ ♪♪♪

❤❤❤ 致力学好编程的宝藏博主,代码兴国!❤❤❤

        哈咯各位铁汁们,大家新年过得快乐吗?反正大伟是过得很快乐,天天就是玩玩玩,吃吃吃 (^▽^ )。不过堕落的生活不能持续太久,所以从今天开始大伟又要继续更新我们的数据结构啦!啪叽啪叽o( ̄ε ̄*)。

        OK,那么今天要学的就是我们数据结构中很重要的一环:单链表

        那首先,我们需要知道链表是什么东西:

概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 

        形象一点就是:链表就是一列火车,链表中的每一个节点就是火车的每一节车厢,每一个车厢都是单独存在的,而每两个车厢都是有前后联系的,为了进入中间的某节车厢就需要从这节车厢的上一节或下一节进入。

        那在链表中,这些车厢是什么样子的呢:c0ec554290624d7cb57da6765c7b275e.png

        与顺序表不同的是,链表⾥的每节"⻋厢"都是独⽴申请下来的空间,我们称之为“结点/节点”, 节点的组成主要有两个部分:当前节点要保存的数据和保存下⼀个节点的地址(指针变量)。  

        图中指针变量plist保存的是第⼀个节点的地址,我们称plist此时“指向”第⼀个节点,如果我们希望plist“指向”第⼆个节点时,就需要指针变量来保存下⼀个节点的位置。那该如何做呢?

        结合前⾯学到的结构体知识,我们可以给出每个节点对应的结构体代码:

struct SListNode
{int data; //节点数据 struct SListNode* next; //指针变量⽤保存下⼀个节点的地址 
};

        当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数 据,也需要保存下⼀个节点的地址(当下⼀个节点为空时保存的地址为空)。

         而当我们想要从第⼀个节点⾛到最后⼀个节点时,只需要在前⼀个节点拿上下⼀个节点的地址(下⼀个 节点的钥匙)就可以了。

        和循序表类似,我们的单链表也有增删查改的功能,那接下来就开码把!

        当然了,首先我们还是开三个文件:SL.h  SL.c  test.c 。功能的话不需要多说了吧~

        SL.h

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<malloc.h>
typedef int SLDataTpye;
typedef struct SListNode
{SLDataTpye data;struct SListNode* next;
}SLNode;void SLPrint(SLNode* phead);
//打印链表
SLNode* SLBuyNode(SLDataTpye x);
//创建链表节点
void SLPushFront(SLNode** pphead, SLDataTpye x);
//头插
void SLPushBack(SLNode** pphead, SLDataTpye x);
//尾插
void SLPopFront(SLNode** pphead);
//头删
void SLPopBack(SLNode** pphead);
//尾删
SLNode* SLFind(SLNode* phead, SLDataTpye x);
//查找链表元素
void SLInsertFront(SLNode** pphead, SLNode* pos, SLDataTpye x);
//在链表某位置前插入
void SLInsertAfter(SLNode** pphead, SLNode* pos, SLDataTpye x);
//在链表某位置后插入
void SLErase(SLNode** pphead, SLNode* pos);
//删除某位置(下标)元素
void SLEraseAfter(SLNode* pos);
//删除某位置元素(下标)后面一个位置的元素
void SLDestroy(SLNode* phead);
//摧毁链表

        SL.c

                SLPrint

void SLPrint(SLNode* phead)
{SLNode* cur = phead;
//cur指向头结点,然后依次往下走while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL");
}

                SLBuyNode

SLNode* SLBuyNode(SLDataTpye x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
//每次开辟一个空间if (newnode == NULL){perror("malloc");return;}
//若开辟失败newnode->data = x;newnode->next = NULL;return newnode;
}

                 SLPushFront

void SLPushFront(SLNode** pphead, SLDataTpye x)
{assert(pphead);SLNode* newnode = SLBuyNode(x);
//在链表头结点前插入,然后将头结点设置为新插入的节点newnode->next = *pphead;*pphead = newnode;
}

                SLPushBack

void SLPushBack(SLNode** pphead, SLDataTpye x)
{assert(pphead);SLNode* newnode = SLBuyNode(x);
//若链表为空则直接将新的节点设置为头结点if (*pphead == NULL){*pphead = newnode;}
//若链表不为空则循环找到最后一个节点,再将新创建的节点接到尾节点后面else{SLNode* tail = *pphead;while (tail->next){tail = tail->next;}tail->next = newnode;}
}

                SLPopFront

void SLPopFront(SLNode** pphead)
{assert(pphead);assert(*pphead);
//让cur保存当前头结点,然后直接让头结点往后走一个,再释放刚刚保存的cur的空间(因为释放了空间且当前空间不再使用,所以里面的值也不需要关心了)SLNode* cur = *pphead;*pphead = (*pphead)->next;free(cur);
}

                SLPopBack

void SLPopBack(SLNode** pphead)
{assert(pphead);assert(*pphead);
//若链表只有一个元素,则将链表置空if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}
//若链表有多余一个元素,找尾节点的前一个节点,再释放掉尾节点else{SLNode* tail = *pphead;while (tail->next->next){tail = tail->next;}free(tail->next);tail->next = NULL;}
}

                SLFind

SLNode* SLFind(SLNode* phead, SLDataTpye x)
{SLNode* cur = phead;
//直接while循环找,若找到则返回此节点,若找不到则返回空while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

                SLInsertFront

void SLInsertFront(SLNode** pphead, SLNode* pos, SLDataTpye x)
{assert(pphead);assert(pos);
//若插入的位置正好是头结点,则直接头插if (pos == *pphead){SLPushFront(pphead, x);}
//若不是,则找到需要插入节点的前一个节点,然后将值为x的节点插入中间else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLNode*newnode = SLBuyNode(x);newnode->next = pos;prev->next = newnode;}
}

                SLInsertAfter

void SLInsertAfter(SLNode** pphead, SLNode* pos, SLDataTpye x)
{assert(pphead);assert(pos);SLNode* newnode = SLBuyNode(x);
//注意顺序,要先将newnode的next指向pos的next,不然会找不到pos的next节点(大家可以想一想为什么)newnode->next = pos->next;pos->next = newnode;
}

                SLErase

oid SLErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(pos);
//若要删除的位置为头结点,则直接头删if (pos == *pphead){SLPopFront(pphead);}
//否则则找到此节点的前一个节点,然后让前一个节点的next指向当前节点的next,再释放当前节点的空间else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);}
}

                SLEraseAfter

void SLEraseAfter(SLNode* pos)
{assert(pos);assert(pos->next);
//直接改变当前节点的next指向(但是要保存当前节点的next,否则不好释放空间)SLNode* next = pos->next;pos->next = pos->next->next;free(next);
}

                SLDestroy

void SLDestroy(SLNode* phead)
{SLNode* cur = phead;
//全部空间释放while (cur){SLNode* next = cur->next;free(cur);cur = next;}
}

                OKOK,以上就是我们的整体代码的主逻辑的实现,那我们再来测试一下吧!

        test.c

#include "SL.h"
void test1()
{SLNode* plist = NULL;SLPushFront(&plist, 5);//5->NULLSLPushFront(&plist, 2);//2->5->NULLSLPushBack(&plist, 7);//2->5->7->NULLSLPushBack(&plist, 9);//2->5->7->9->NULLSLPopBack(&plist);//2->5->7->NULLSLPopFront(&plist);//5->7->NULLSLNode* pos = SLFind(plist, 7);SLInsertFront(&plist, pos, 40);//5->40->7->NULLSLInsertAfter(&plist, pos, 40);5->40->7->40->NULLSLPrint(plist);printf("\n");SLPrint(pos);//SLErase(&plist, pos);//printf("\n");//SLPrint(plist);SLEraseAfter(pos);//5->40->7->NULLprintf("\n");SLPrint(plist);
}
int main()
{test1();return 0;
}

        那我们来看看我们输出的结果是不是和我们的预期一样的呢: 

ce646faee7f64ac08600a333465a6725.png

        可以看到啊,答案是完全对得上的,所以此刻我们的单链表就完美实现啦(大家私下可以自己试试)!

        在最后我给大家提前预支一下:其实单链表一共有八种:507b1f02a77449279076150bf5862a96.png 

        分别画图示意:85003a8fec23445abc71474c66995537.png 85141f9e7380414fb3832f059111b82d.png8d735d6594034f0aa5414d901a6f2837.png

        那铁汁们来分析一下,我们今天写的单链表是属于哪一种的呢?(σ`д′)σ

         对咯,就是单向不带头不循环链表,而这种链表我们通常把它称为单链表,既然我们有八种链表,各位铁汁们,我们私下自己来想一想其他几种链表该怎么实现呢? 大伟将在下一篇博客里给大家介绍单链表的死对头:双向带头循环链表 。请敬请期待哦~

        Genius only means hard-working all one's life. 天才只意味着终身不懈地努力

        本篇博客也就到此为止了,送大家一碗鸡汤,勉励自己以及这世界上所有追逐梦想的赤子趁年华尚好努力提升自己,莫欺少年穷!           841e1d85be4844fbb0e088ce2c791d5c.jpeg

 

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

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

相关文章

面试redis篇-02缓存穿透

原理 例&#xff1a; 一个get请求&#xff1a;api/news/getById/1 缓存穿透&#xff1a;查询一个不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致每次请求都查数据库 解决方案一 缓存空数据&#xff0c;查询返回的数据为空&#xff0c;仍把…

Axure9必备函数(2)

字符串的属性和函数 length:返回字符串的字符长度&#xff0c;包括空格&#xff0c;注意:汉字占1个字符charAt(index):返回字符串中指定索引处的字符indexOf(searchValue"):返回调用函数的字符串中搜索值的第一个实例的起始索引。如果未找到搜索值&#xff0c;则返回 -1la…

ABAQUS应用04——集中质量的添加方法

文章目录 0. 背景1. 集中质量的编辑2. 约束的设置3. 总结 0. 背景 混塔ABAQUS模型中&#xff0c;机头、法兰等集中质量的设置是模型建立过程中的一部分&#xff0c;需要研究集中质量的添加。 1. 集中质量的编辑 集中质量本身的编辑没什么难度&#xff0c;我已经用Python代码…

Django后端开发——模型层及ORM介绍

文章目录 参考资料Django配置MySQL安装mysqlclient创建数据库进入数据库的操作可能遇到的问题及解决方案Pycharm配置settings.py 模型![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c83753397bf6481d8defde26537903bf.png)ORM介绍示例终端&#xff1a;settings.py…

Linux——开发工具的使用

目录 Linux软件包管理器 yum rzsz Linux编辑器——vim vim的使用 vim的基本操作 命令模式的常见命令 底行模式的常见命令 vim是需要配置的 Linux编译器——gcc/g 预处理 编译 汇编 链接 函数库 Linux项目自动化构建工具 make/makefile make原理 项目清理 Linux调试器g…

踩坑实录(Fourth Day)

今天开工了&#xff0c;其实还沉浸在过年放假的喜悦中……今天在自己写 Vue3 的项目&#xff0c;虽说是跟着 B 站在敲&#xff0c;但是依旧是踩了一些个坑&#xff0c;就离谱……照着敲都能踩到坑&#xff0c;我也是醉了…… 此为第四篇&#xff08;2024 年 02 月 18 日&#x…

天锐绿盾|公司办公文件数据\资料自动智能透明加密——数据防泄漏系统

天锐绿盾是一款专注于企业电脑文件数据资料透明加密的防泄密系统。它采用了先进的文件过滤驱动透明加解密技术&#xff0c;可以对企业核心部门的文档进行强制加密处理&#xff0c;确保从文档创建开始就能自动加密保护。文件的保存加密、打开解密完全由后台加解密驱动内核自动完…

2024年热门项目持续分析,趣味测试小程序项目分析以及系统功能介绍

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 趣味测试小程序是目前比较热门的娱乐测试&#xff0c;运行模式就是引导粉丝用户进去…

【《高性能 MySQL》摘录】第 3 章 服务器性能剖析

文章目录 3.1 性能优化简介3.1.1 通过性能剖析进行优化3.1.2 理解性能剖析 3.2 对应用程序进行性能剖析3.3 剖析 MySQL 查询3.3.1 剖析服务器负载捕获 MySQL 的查询到日志文件中分析查询日志 3.3.2 剖析单挑查询使用 SHOW PROFILE &#xff08;现已过时&#xff09;使用SHOW ST…

Win32 获取EXE/DLL文件版本信息

CFileVersion.h #pragma once#include <windows.h> #include <string> #include <tchar.h>#ifdef _UNICODE using _tstring std::wstring; #else using _tstring std::string; #endif// 版本号辅助类 class CVersionNumber { public:CVersionNumber() :m_…

【天衍系列 05】Flink集成KafkaSink组件:实现流式数据的可靠传输 高效协同

文章目录 01 KafkaSink 版本&导言02 KafkaSink 基本概念03 KafkaSink 工作原理1.初始化连接2.定义序列化模式3.创建KafkaSink算子4.创建数据源5.将数据流添加到KafkaSink6.内部工作机制 04 KafkaSink参数配置05 KafkaSink 应用依赖06 KafkaSink 快速入门6.1 包结构6.2 项目…

【深度学习:数据注释工具】构建与购买:从业者的经验教训

【深度学习&#xff1a;数据注释工具】构建与购买&#xff1a;从业者的经验教训 为什么数据注释团队需要标记工具&#xff1f;您的软件工程团队是否有时间/资源来构建数据注释解决方案&#xff1f;为您的项目构建数据注释工具需要多长时间&#xff1f;将开发外包给第三方会更有…

SSH密钥认证登陆流程(Vscode连接到远程)

目录 前言连接远程步骤1. 下载工具包wsCli到本地机器2. 本地机器上生成ssh密钥3. 在服务器上安装公钥4. vscode连接到远程 参考资料 前言 SSH&#xff08;Secure Shell&#xff09;是一种用于远程登录和安全传输数据的网络协议。它提供了两种主要的远程连接方式&#xff1a; 密…

有趣的CSS - 新拟态输入框

我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;《有趣的css》系列已更新 11 篇了&#xff0c;今天这篇是关于新拟态风格的一个输入框效果&#xff0c;希望你们喜欢。 目录 页面效果核心代码html代码css代码 完整代码html页面css样式 页面效果 此效果使用 css 中 b…

PDF控件Spire.PDF for .NET【安全】演示:如何在 PDF 中添加签名字段

Spire.PDF for .NET 是一款独立 PDF 控件&#xff0c;用于 .NET 程序中创建、编辑和操作 PDF 文档。使用 Spire.PDF 类库&#xff0c;开发人员可以新建一个 PDF 文档或者对现有的 PDF 文档进行处理&#xff0c;且无需安装 Adobe Acrobat。 E-iceblue 功能类库Spire 系列文档处…

PyCharm 新建目录 (directory or folder)

PyCharm 新建目录 [directory or folder] 1. 新建目录2. Enter new directory name -> OKReferences 1. 新建目录 right mouse click on the project -> New -> Directory 2. Enter new directory name -> OK ​​​ References [1] Yongqiang Cheng, https:/…

go redis

go redis 快速入门 安装&#xff1a; go get github.com/redis/go-redis/v9然后创建客户端&#xff1a; package mainimport "github.com/redis/go-redis/v9"func main() {rdb : redis.NewClient(&redis.Options{Addr: "47.109.87.142:6379",Pa…

C++_design_model_observer

/* 观察者模式是一种常用的设计模式&#xff0c;用于在对象之间建立一种一对多的依赖关系&#xff0c;当被观察的对象发生变化时&#xff0c; 所有依赖于它的对象都能够得到通知并自动更新。下面是一个使用C实现观察者模式的例子&#xff1a; */#include <iostream> #inc…

Redis篇----第六篇

系列文章目录 文章目录 系列文章目录前言一、Redis 的持久化机制是什么?各自的优缺点?二、Redis 常见性能问题和解决方案:三、redis 过期键的删除策略?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章…

如何查看springboot依赖的JDK版本

通过maven构建Java项目或者使用源代码进行Java编译时&#xff0c;常常遇到JDK版本和Springboot版本不匹配的问题&#xff0c;导致编译失败&#xff0c;比如出现如下错误&#xff1a; org/springframework/beans/factory/InitializingBean.class [ERROR] 类文件具有错误的…