数据结构初阶-单链表

        链表的结构非常多样,以下情况组合起来就有8种(2 x 2 x 2)链表结构:

        而我们主要要熟悉的单链表与双向链表的全称分别为:不带头单向不循环链表,带头双向循环链表,当我们对这两种链表熟悉后,便能不费力的推出另外四种链表。我们本文主要介绍单链表。

 一,单链表的概念与结构

1.1概念       

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

        这里我们可以联想平时我们所坐的高铁或火车,淡季时车次的车厢会相应减少,旺季时车次的车厢会额外增加几节。只需要将火车里的某节车厢去掉/ 加上,不会影响其他车厢,每节车厢都是独立存在的。

        那,在链表中我们的火车车厢又是怎么样的:

 1.2结点

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

        图中指针变量 plist保存的是第⼀个结点的地址,我们称plist此时“指向”第⼀个结点,如果我们希望 plist“指向”第⼆个结点时,只需要修改plist保存的内容为0x00B0。

1.3链表的性质

1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续

2、结点⼀般是从堆上申请的

3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

        结合前⾯学到的结构体知识,我们可以给出每个结点对应的结构体代码:(因为我们存储的数据类型不定,所以我们可以先将我们要存储的数据类型先定义下):

typedef int SLDataType;typedef struct SLTistNode
{SLDataType Data;struct SLTistNode* next;
}SLTNode,*PSLTNode;

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

接下来让我们来实现单链表的常用功能,实现的功能大多与顺序表类似:

二,单链表功能的实现

2.1链表的初始化

        在介绍它的初始化之前,我们先来介绍什么是带头(哨兵位)和不带头,对于带头,则是链表头部初始时便有一个哨兵位节点,但不存放任何数据,由于我们的单链表为不带头链表,所以我们这里初始化直接引用上面的结构体,设置我们的链表头为空指针即可:

PSLTNode plist = NULL;

2.2获取新节点函数SLTBuyNode

       在对链表进行头尾插之前,由于我们每次插入节点都需要创建新的节点,所以我们则合理直接分装一个函数,后面实现头尾插功能直接调用即可,老样子,我们先给出代码:

PSLTNode SLTBuyNode(SLDataType x)
{PSLTNode node = (PSLTNode)malloc(sizeof(SLTNode));if (node == NULL){perror("newnode:");exit(1);}node->Data = x;node->next = NULL;return node;
}

       由于我们的链表内存的大小是动态变化的,所以我们需要使用malloc函数来开辟节点的空间。为了确保我们代码的健壮性,我们需要检验空间开辟是否成功。然后将所需要插入的数据赋值给新节点的data,同时将新节点的next置为空。

2.3尾插函数SLTPushBack

       由于我们这里是在链表的尾端进行尾插,所以我们直接将当前的链表尾节点的指针设置为我们要创建的新节点的地址:

void SLTPushBack(PSLTNode* pphead, SLDataType x)
{assert(pphead);PSLTNode newcode = SLTBuyNode(x);if (*pphead == NULL){*pphead = newcode;}else{PSLTNode pcur = *pphead;while (pcur->next){pcur = pcur->next;}pcur->next = newcode;}
}

        由于我们初次插入节点时,链表此时为空,所以我们在其为空时直接将指向新节点的指针设置为指向链表头节点的指针,但尾插我们需要遍历链表找到尾节点,所以时间复杂度为O(N)。

2.4头插函数SLTPushFront

       相对于尾插法,我们的头插法就简单许多,只需要将要插入的新节点的next指针直接设置为指向链表头节点的指针,然后再将头节点的指针设置为指向新节点的指针即可:

void SLTPushFront(PSLTNode* pphead, SLDataType x)
{assert(pphead);PSLTNode newcode = SLTBuyNode(x);if (*pphead == NULL){*pphead = newcode;}else{newcode->next = *pphead;*pphead = newcode;}
}

  我们可以明显的看到,头插法的方式没有遍历数组,它的时间复杂度为O(1)。

2.5尾删函数SLTPopBack 

       在进行尾删之前,我们需要判断链表中是否还有节点,如果没有节点,我们就不能再对链表进行删除:

void SLTPopBack(PSLTNode* pphead)
{assert(pphead && *pphead);PSLTNode pcur = *pphead;PSLTNode pcur1 = NULL;while (pcur->next){pcur1 = pcur;pcur = pcur->next;}if (pcur1){pcur1->next = NULL;free(pcur);pcur = NULL;}else{free(*pphead);*pphead = NULL;}
}

        首先我们要遍历链表找到尾节点,我们可以先对头节点的NEXT判空,如果为空,则说明链表中的节点只有一个,我们直接释放头节点即可。如果不为空,我们则直接循环找到尾节点,对其进行释放,同时将其上一个节点的NEXT置为空。

2.6头删函数SLTPopFront

         老样子,我们需要先判断链表是否还有节点,有直接先记住头节点的指针,然后让头指针指向它的NEXT即可:

void SLTPopFront(PSLTNode* pphead)
{assert(pphead && *pphead);PSLTNode pcur = *pphead;*pphead = pcur->next;free(pcur);pcur = NULL;
}

       我们在这里会发现,头删相比于尾删也简单很多,所以当我们在没有特殊要求的情况下,尽量使用头删/插对链表进行增删操作,这样复杂度会少很多。

2.7寻找指定节点函数SLTFind

       在实现删除/增加指定位置数据之前,由于这两个函数都需要寻找数据,所以我们可以直接将寻找函数分装为一个函数,便于我们后面直接调用:

PSLTNode SLTFind(PSLTNode phead, SLDataType x)
{assert(phead);PSLTNode pcur = phead;while (pcur){if (pcur->Data == x){return pcur;}pcur = pcur->next;}return NULL;
}

       如果没有找到目标数据,我们直接返回空指针即可,同时我们需要遍历链表一个一个去寻找,这是我们常用的寻找方法。

2.8向指定位置之后插入数据函数SLTInsertAfter

       我们这里不介绍在之前插入数据的原因是因为此时我们需要遍历链表去找到指定位置前方的数据,所以复杂度会高很多,而这里我们删除指定位置之后则不需要遍历链表:

void SLTInsertAfter(PSLTNode pos, SLDataType x)
{assert(pos);PSLTNode newcode = SLTBuyNode(x);newcode->next = pos->next;pos->next = newcode;
}

需要注意的即是对NEXT指针指向的改变,实现难度不高。

2.9删除指定位置之后的数据函数SLTEraseAfter

 与之前的删除一样,我们需要先判断链表中是否有节点:

void SLTEraseAfter(PSLTNode pos)
{assert(pos && pos->next);PSLTNode del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}

  会了我们上面的头删与尾删函数之后,其实这部分很简单。

2.10链表的销毁函数SListDestroy 

void SListDestroy(PSLTNode* pphead)
{assert(pphead && *pphead);PSLTNode pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

这里我们也需要判断链表是否为空,如果为空我们也就没必要释放链表空间 。

三,几道单链表OJ题推荐

203. 移除链表元素 - 力扣(LeetCode)

206. 反转链表 - 力扣(LeetCode)

876. 链表的中间结点 - 力扣(LeetCode)

21. 合并两个有序链表 - 力扣(LeetCode)

链表分割_牛客题霸_牛客网 (nowcoder.com)

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)

160. 相交链表 - 力扣(LeetCode)

141. 环形链表 - 力扣(LeetCode)

142. 环形链表 II - 力扣(LeetCode)

       这里尤其是最后两道,使用了Floyd龟兔赛跑算法,非常推荐去练习学习 。

       

 

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

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

相关文章

重生之我们在ES顶端相遇第5章-常用字段类型

思维导图 前置 在第4章,我们提到了 keyword(一笔带过)。在本章,我们将介绍 ES 的字段类型。全面的带大家了解 ES 各个字段类型的使用场景。 字段类型 ES 支持以下字段类型(仅介绍开发中常用,更多内容请自…

大模型之RAG-关键字检索的认识与实战(混合检索进阶储备)

前言 按照我们之前的分享(大模型应用RAG系列3-1从0搭建一个RAG:做好文档切分): RAG系统搭建的基本流程 准备对应的垂域资料文档的读取解析,进行文档切分将分割好的文本灌入检索引擎(向量数据库&#xff…

AI App Store-AI用户评价-多维度打分对比pk-AI社区

C端用户、创作者、AI达人们在选择众多国内外AI厂商的服务时候往往感到一头雾水,那么多功能接近的AI应用(智能对话类、文档总结类、文生图、AI搜索引擎) 究竟在不同用户需求场景下表现怎么样。大部分人如果有需求都会所有平台都尝试一遍,比如一个博主生成…

Linux内网离线用rsync和inotify-tools实现文件夹文件单向同步和双向同步

lsyncd实现方式可参考:https://www.jianshu.com/p/c075ccf89516 安装文件下载:相关文件下载 rsync默认都有,所以没有提供。 服务端和客户端均操作 服务端:双向同步其实都是服务端,只是单向同步时稍有区别 客户端&am…

C++自定义字典树结构

代码 #include <iostream> using namespace std;class TrieNode { public:char data;TrieNode* children[26];bool isTerminal;TrieNode(char ch){data ch;for (int i 0; i < 26; i){children[i] NULL;}isTerminal false;} }; class Trie { public:TrieNode* ro…

Android、Java反编译工具JADX

目录 介绍 主要特点: jadx-gui特性: 下载地址 使用 介绍 jadx - Dex to Java反编译器 用于从Android Dex和Apk文件生成Java源代码的命令行和GUI工具 请注意,在大多数情况下,jadx不能100%反编译所有的代码,所以会出现错误。 有关变通方法,请参阅故障排除指南。 目前…

返回倒数第 k 个节点 - 力扣(LeetCode)

面试题 02.02. 返回倒数第 k 个节点 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/int kthToLast(struct ListNode* head, int k) {struct ListNode* fastnode head…

git面面观,面试题,常见问题

1. 简述什么是Git &#xff1f; Git是一款分布式源代码管理工具(版本控制工具) 。 Git得其数据更像是一系列微型文件系统的快照。使用Git&#xff0c;每次提交或保存项目状态时&#xff0c;Git基本上都会记录当时所有文件的外观&#xff0c;并存储对该快照的引用。为了提高效…

Prompt工程:与AI聊天机器人更好地交流

Prompt工程:与AI聊天机器人更好地交流 1. 清楚地说明你想要什么2. 告诉AI它现在是谁3. 一步一步来4. 给AI一些例子5. 让AI检查自己的回答6. 把AI当作你的小助手7. 让AI帮你想主意8. 让AI告诉你它需要知道什么9. 教AI一步一步思考结语 大家好!今天我们来聊聊如何跟AI聊天机器人更…

互三群危害?如何才能正确上热榜。

前言 攀登热门榜单之巅&#xff0c;历来是才华与智慧较量的舞台&#xff0c;策略与努力的结晶。然而&#xff0c;在这片看似光鲜的网络世界里&#xff0c;也潜藏着不为人知的暗流——“互三群”的歪风邪气。揭露其真面目&#xff0c;以正网络风气&#xff0c;是每一位网络创作…

基于区块链技术的中药饮片代煎配送服务与监管平台

业务背景 近年来&#xff0c;随着公众对中医药青睐有加&#xff0c;中药代煎服务作为中医药现代化的重要一环&#xff0c;在全国各地蓬勃兴起。鉴于传统煎煮方式的繁琐耗时&#xff0c;医疗机构纷纷转向与第三方中药饮片企业合作&#xff0c;采用集中代煎模式。这些第三方煎药中…

Proactor模型

文章目录 概述1. 异步I/O操作2. 事件通知3. 事件处理函数4. 事件循环5. 多线程支持6. 非阻塞I/O7. 可扩展性8. 错误处理9. 资源管理10. 编程复杂性11. 应用场景流程图 结论 概述 Proactor模型是一种基于异步I/O操作的事件驱动编程模型&#xff0c;主要用于处理并发的I/O事件&a…

冒泡排序(数组作为函数参数)

什么是冒泡排序&#xff1f; 冒泡排序&#xff08;Bubble Sort&#xff09;也是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换&#xff0c;…

【Unity】RPG2D龙城纷争(十五)特殊加成型要诀

更新日期:2024年7月22日。 项目源码:第五章发布(正式开始游戏逻辑的章节) 索引 简介一、特殊加成型要诀基类二、扩充要诀数据集三、移动寻路时,应用特殊加成效果四、攻击寻路时,应用特殊加成效果五、攻击别人时,应用特殊加成效果六、被别人攻击时,应用特殊加成效果七、…

docker--容器数据进行持久化存储的三种方式

文章目录 为什么Docker容器需要使用持久化存储1.什么是Docker容器&#xff1f;2.什么是持久化存储&#xff1f;3.为什么Docker容器需要持久化存储&#xff1f;4.Docker如何实现持久化存储&#xff1f;(1)、Docker卷(Volumes)简介适用环境:使用场景:使用案例: (2)、绑定挂载&…

pycharm+pytorch2.3.1安装

成功运行 Anaconda简介 Anaconda 就是可以便捷获取包且对包能够进行管理&#xff0c;同时对环境可以统一管理的发行版本。Anaconda包含了conda、Python在内的超过180个科学包及其依赖项。 Anaconda安装 去官网地址下载 Download Anaconda Distribution | Anaconda​www.ana…

PHP常量

PHP 常量是在脚本执行期间其值不会改变的量。它们通常用于存储不经常改变的值&#xff0c;如配置选项、数据库连接信息等。在 PHP 中&#xff0c;常量与变量不同&#xff0c;一旦定义就不能被重新定义或取消定义&#xff08;直到脚本执行结束&#xff09;。下面是关于 PHP 常量…

SpringBoot启动原理详解

透彻理解SpringBoot启动原理&#xff08;一&#xff09; 一张Spring启动顺序图我们对Spring启动原理有多少理解呢一起看一下Spring有那些扩展点和启动过程有关通过打印日志学习Spring的执行顺序实例化和初始化的区别Spring重要扩展点的启动顺序1.BeanFactoryPostProcessor2.实例…

python3.10.4——Windows环境安装

python下载官网&#xff1a;https://www.python.org/downloads/ 如果安装在C盘&#xff0c;需要右键→选择“以管理员身份运行” 勾选2个按钮&#xff0c;选择自定义安装 全部选择&#xff0c;点击Next 更改安装路径 命令行检查python是否安装成功&#xff1a; 出现版本号说明…

内存泄漏详解

文章目录 什么是内存泄漏内存泄漏的原因排查及解决内存泄漏避免内存泄漏及时释放资源设置合理的变量作用域及时清理不需要的对象避免无限增长避免内部类持有外部类引用使用弱引用 什么是内存泄漏 内存泄漏是指不使用的对象持续占有内存使得内存得不到释放&#xff0c;从而造成…