数据结构知识点总结-线性表(3)-双向链表定义、循环单链表、、循环双向链表、静态链表、顺序表与链表的比较

news2024/9/30 1:32:34

双向链表定义

        单链表结点中只有一个指向其后继的指针,这使得单链表只能从头结点依次顺序地向后遍历。若要访问某个结点的前驱结点(插入、删除操作时),只能从头开始遍历,访问后继结点的时间复杂度为 O(1) , 访问前驱结点的时间复杂度为 O( n ) 。

        为了克服单链表的删除缺点,引入了双向链表,双向链表结点中有两个指针 prior 和 next,分别指向其前驱结点和后继结点。

双链表中结点类型的描述如下:
typedef struct DNode {                // 定义双链表结点类型
    ElemType  data ;                  // 数据域
    struct  DNode  *prior , * next ;  // 前驱和后继指针
}DNode , * DLinkList ;

        双链表仅仅是在单链表结点中增加了一个指向其前驱的 prior 指针,因此,在双链表中执行按值查找和按位查找的操作和单链表相同。

        但双链表在插入和删除操作的实现上,和单链表有着较大的不同。这是因为“链”变化时也需要对 prior 指针做出修改,其关键在于保证在修改的过程中不断链。此外,双链表可以很方面地找到其前驱结点,因此不管前插入、后插入、前删除、后阐述,算法的时间复杂度均为为 O(1) 。

1)双向链表的插入操作

第一步: s->next = p->next ;   // 将结点 *s 插入到结点 *p 之后

第二步: p->next->prior  = s ;

第三步: s->prior = p ;

第四步: p->next = s ;

        上面的代码的语句顺序不是唯一的,但也不是任意的,第一步和第二步必须在第四步之前,否则 *p 的后继结点的指针就丢掉了,导致插入失败。

(2)删除操作

删除双链表中结点 *p 的后继结点 *q ,其指针的变化过程如下图:

 删除操作的代码片段如下:

p->next = q->next ;             // 上图中的第一步
q->next->prior = p ;            // 上图中的第二步
free( q ) ;                     // 释放结点空间

循环单链表

        循环单链表和单链表的区别在于,表中最后一个结点指针不是 NULL ,而改为指向头结点,从而整个链表形成了一个环,如下图所示:

        在循环单链表中,表尾结点 *r 的 next 域指向 L ,故表中没有指针域为 NULL的结点,因此,循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针

        在单链表中只能从表头结点开始往后顺序遍历整个链表,而循环单链表可以从表中的任一结点开始遍历整个链表。有时对单链表常做的操作是在表头和表尾进行的,此时可以对循环单链表不设头指针而仅设尾指针,从而使得操作效率更高。其原因是若设的是头指针,对表尾进行操作需要 O( n ) 的时间复杂度,而如果设的是尾指针 r , r->next 即为头指针,对于表头与表尾进行操作都只需要 O( 1 ) 的时间复杂度。

循环双向链表

        由循环单链表的定义不难推出循环双链表,不同的是在循环双链表中,头结点的 prior 指针还要指向表尾结点,如下图所示:

        在循环双链表 L 中,某结点 *p 为尾结点时, p->next = = L ; 当循环双链表为空表时,其头结点的 prior next 域都等于 L

静态链表

        静态链表是借助数组来描述线性表的链式存储结构,结点也有数据域 data 和 指针域 next ,与前面所讲的链表中的指针不同的是,这里的指针是结点的相对地址(数组下标),又称为游标。和顺序表一样,静态链表也要预先分配一块连续的内存空间。

        静态链表和单链表的对应关系如下图:

静态链表结构类型的描述如下:

# define MaxSize 50          // 静态链表的最大长度
    typedef  struct {        // 静态链表结构类型的定义
    ElemType  data ;         // 存储数据元素
    int  next ;              // 下一个元素的数组下标
} SLinkList[ MaxSize ] ;

        静态链表以 next == -1 作为其结束的标志。静态链表的插入、删除操作与动态链表相同,

        只需要修改指针,而不需要移动元素。总体来说,静态链表没有单链表使用起来方便,但是在一些不支持指针的高级语言(如 Basic)中 ,这又是一种非常巧妙的设计方法。

        顺序表和静态链表的物理结构(即存储结构)是相同的,在计算机内存中以数组的形式实现,是用一组地址连续的存储单元依次存储数据元素的线性结构,但两者的数据结构(逻辑结构)是不同的。

        静态链表不是顺序结构,这里的结构指的是逻辑结构,在逻辑结构层面,静态链表是链式结构。在物理层面,都采用顺序形式保存数据,因此物理结构、存储结构与线性表的顺序存储相同。

顺序表和链表的比较(数组与链表)

1)存取方式

顺序表可以顺序存取,也可以随机存取,链表只能从表头顺序存取元素。

2)逻辑结构和物理结构

采用顺序存储时,逻辑上相邻的元素,其对应的物理存储位置也相邻。

而采用链式存储时,逻辑上相邻的元素,其物理存储位置则不一定相邻,其对应的逻辑关系是通过指针链接来表示的,静态链表也是链式存储方式。

注意区别存取方式存储方式,存取方式指的插入删除

3)查找、插入和删除操作

           对于按值查找,当顺序表在无序的情况下,两者的时间复杂度均为 O( n ) ; 而当顺序表有序时,可采用折半查找,此时时间复杂度为O(lgN)

对于按序号查找,顺序表支持随机访问,时间复杂度为 O(1),而链表不支持随机访问,只能遍历链表,其平均时间复杂度为O(n) 。顺序表的插入、删除操作,平均需要移动半个表长的元素。链表的插入、删除操作,只需要修改相关结点的指针域即可。由于链表每个结点带有指针域,因而在存储空间上比顺序存储要付出较大的代价,存储密度不如顺序存储。

4)空间分配

顺序存储在静态存储分配情形下,一旦存储空间装满就不能扩充,如果再加入新元素将出现内存溢出,需要预先分配足够大的存储空间。预先分配过大,可能会导致顺序表后部大量闲置;预先分配过小,又会造成溢出。动态存储分配虽然存储空间可以扩充,但需要移动大量元素,导致操作效率降低,而且若内存中没有更大块的连续存储空间将导致分配失败。

链式存储的结点空间只在需要的时候申请分配,只要内存有空间就可以分配,操作灵活、高效。

在实际中应该怎样选取存储结构?

1、基于存储的考虑

对线性表的长度或存储规模难以估计时,不宜采用顺序表;链表不用事先估计存储规模。

2、基于运算的考虑

在顺序表中按序号访问 a[i] 的时间复杂度为O(1) ,而链表中按序号访问的时间复杂度为 O(n) ,所以如果经常做的运算是按序号访问数据元素,显然顺序表优于链表。

在顺序表中做插入、删除操作时,平均移动表中一半的元素,当数据表较长时,这一点是不应忽视的;在链表中做插入、删除操作时,虽然也要找插入位置,但是操作是比较简单的,从这个角度考虑链表优于顺序表。

3、基于环境的考虑

顺序表容易实现,任何高级语言中都有数组类型;链表的操作是基于指针的,相对来讲,前者实现较为简单,这也是用户考虑的一个因素。

总之,两种存储结构各有长短,选择哪一种由实际问题的主要因素决定。通常较稳定的线性表选择顺序存储,而频繁做插入、删除操作的线性表(即动态性较强)宜选择链式存储。

线性表总结结束,下一篇开始介绍栈和队列

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

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

相关文章

详解java类型转换

✨✨ 所属专栏: Java基石:深入探索Java核心基础✨✨ 🎈🎈作者主页: 喔的嘛呀🎈🎈 引言 在Java编程中,类型转换是将一个数据类型的值转换为另一个数据类型的过程。Java中的类型转换主…

Django学习笔记-ModelForm使用(完全依赖)

1.创建模型 ,code,name,sex,entrydate 2.模型映射 python manage.py makemigrations myapp01,python manage.py migrate 3.创建模型表单,继承forms.ModelForm,Meta:元数据,models需引入,fields填写引用的模型变量 4.创建testModelForm.html,添加urls 5.views编写testmodelfo…

前端sql条件拼接js工具

因为项目原因&#xff0c;需要前端写sql&#xff0c;所以弄了一套sql条件拼接的js工具 ​ /*常量 LT : " < ", LE : " < ", GT : " > ", GE : " > ", NE : " ! ", EQ : " ", LIKE : " like &qu…

字典树入门

//本题是一道字典树的模板题 //字典树是一种高效率存储多个字符串的数据结构 //其每个结点的权值代表以该结点结尾的字符串的数量,每条边存储一个字符 //从根结点开始,按某一路径遍历到某一结点,即得到一种字符串,其个数等于当前结点存储的数值 //如从根结点开始,依次走过abc三…

【iOS ARKit】ARWorldMap

ARWorldMap 用于存储 ARSession 检测扫描到的空间信息数据&#xff0c;包括地标&#xff08;Landmark&#xff09;、特征点&#xff08;Feature Point&#xff09;、平面&#xff08;Plane&#xff09;等&#xff0c;以及使用者的操作信息&#xff0c;如使用者添加的 ARAnchor …

【Vue】组件通信组件通信

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;JVM ⛺️稳中求进&#xff0c;晒太阳 组件通信 组件通信&#xff0c;就是指组件与组件之间的数据传递 组件的数据是独立的&#xff0c;无法直接访问其他组件的数据想用其他组件的数据--&…

【深度学习】Pytorch教程(十三):PyTorch数据结构:5、张量的梯度计算:变量(Variable)、自动微分、计算图及其可视化

文章目录 一、前言二、实验环境三、PyTorch数据结构1、Tensor&#xff08;张量&#xff09;1. 维度&#xff08;Dimensions&#xff09;2. 数据类型&#xff08;Data Types&#xff09;3. GPU加速&#xff08;GPU Acceleration&#xff09; 2、张量的数学运算1. 向量运算2. 矩阵…

景联文科技:引领战场数据标注服务,赋能态势感知升级

自21世纪初&#xff0c;信息化战争使战场环境变得更为复杂和难以预测&#xff0c;持续涌入的海量、多样化、多来源和高维度数据&#xff0c;加大了指挥员的认知负担&#xff0c;使其需要具备更强的数据处理能力。 同时&#xff0c;计算机技术和人工智能技术的飞速发展&#xff…

EMQX Enterprise 5.5 发布:新增 Elasticsearch 数据集成

EMQX Enterprise 5.5.0 版本已正式发布&#xff01; 在这个版本中&#xff0c;我们引入了一系列新的功能和改进&#xff0c;包括对 Elasticsearch 的集成、Apache IoTDB 和 OpenTSDB 数据集成优化、授权缓存支持排除主题等功能。此外&#xff0c;新版本还进行了多项改进以及 B…

Gemma谷歌(google)开源大模型微调实战(fintune gemma-2b)

Gemma-SFT Gemma-SFT(谷歌, Google), gemma-2b/gemma-7b微调(transformers)/LORA(peft)/推理 项目地址 https://github.com/yongzhuo/gemma-sft全部weights要用fp32/tf32, 使用fp16微调十几或几十的步数后大概率lossnan;(即便layer-norm是fp32也不行, LLaMA就没有这个问题, …

SpringBoot/Java中OCR实现,集成Tess4J实现图片文字识别

场景 Tesseract Tesseract是一个开源的光学字符识别&#xff08;OCR&#xff09;引擎&#xff0c;它可以将图像中的文字转换为计算机可读的文本。 支持多种语言和书面语言&#xff0c;并且可以在命令行中执行。它是一个流行的开源OCR工具&#xff0c;可以在许多不同的操作系…

【Vue3】插槽使用和animate使用

插槽使用 插槽slot匿名插槽具名插槽插槽作用域简写 动态插槽transition动画组件自定义过渡class类名如何使用animate动画库组件动画生命周期appear transition- group过渡列表 插槽slot 插槽就是子组件中提供给父组件使用的一个占位符父组件可以在这个占位符智能填充任何模板代…

pytest-配置项目不同环境URL

pytest自动化中&#xff0c;在不同环境进行测试&#xff0c;可以将项目中的url单独抽取出来&#xff0c;通过pytest.ini配置文件实现&#xff08;类似postman中的“Environments”&#xff09; 使用步骤&#xff1a; 1&#xff09;安装pytest-base-url插件 pytest-base-url …

【Flink精讲】Flink状态及Checkpoint调优

RocksDB大状态调优 RocksDB 是基于 LSM Tree 实现的&#xff08;类似 HBase&#xff09; &#xff0c;写数据都是先缓存到内存中&#xff0c; 所以 RocksDB 的写请求效率比较高。 RocksDB 使用内存结合磁盘的方式来存储数据&#xff0c;每 次获取数据时&#xff0c;先从内存中 …

Mac下载安装配置运行MySQL

一、打开官网 MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ 1、根据自己的电脑版本下载相对应的MySQL版本&#xff0c;Mac分为ARM和X86两个不同的架构 ​ 不知道自己电脑是ARM还是X86的&#xff0c;如下操作进行查询 uname -a 我的电脑是…

2024年Apache DolphinScheduler RoadMap:引领开源调度系统的未来

非常欢迎大家来到Apache DolphinScheduler社区&#xff01;随着开源技术在全球范围内的快速发展&#xff0c;社区的贡献者 “同仁” 一直致力于构建一个强大而活跃的开源调度系统社区&#xff0c;为用户提供高效、可靠的任务调度和工作流管理解决方案。 在过去的一段时间里&…

代码随想录|学习工具分享

工具分享 画图 https://excalidraw.com/ 大家平时刷题可以用这个网站画草稿图帮助理解&#xff01;如果看题解很蒙或者思路不清晰的时候&#xff0c;跟着程序处理流程画一个图&#xff0c;90%的情况下都可以解决问题&#xff01; 数据结构可视化 https://www.cs.usfca.edu/…

【Java设计模式】一、工厂模式、建造者模式、原型设计模式

文章目录 1、简单工厂模式2、工厂方法模式3、抽象工厂模式4、建造者模式5、原型设计模式 设计模式即总结出来的一些最佳实现。23种设计模式可分为三大类&#xff1a; 创建型模式&#xff1a;隐藏了创建对象的过程&#xff0c;通过逻辑方法进行创建对象&#xff0c;而不是直接n…

逆序或者正序打印一个数的每一位数,递归实现(C语言)

从键盘上输入一个不多于5位&#xff08;包括5位&#xff09;的正整数&#xff0c;要求 &#xff08;1&#xff09;求出它是几位数&#xff1b;&#xff08;2&#xff09;分别输出每一位数字&#xff08;3&#xff09;按逆序输出各位数字 &#xff08;1&#xff09;求出它是几位…

Linux浅学笔记04

目录 Linux实用操作 Linux系统下载软件 yum命令 apt systemctl命令 ln命令 日期和时区 IP地址 主机名 网络传输-下载和网络请求 ping命令 wget命令 curl命令 网络传输-端口 进程 ps 命令 关闭进程命令&#xff1a; 主机状态监控命令 磁盘信息监控&#xff1a…