百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

算法与数据结构线性表的顺序存储与链式存储(Swift版)

toyiye 2024-09-06 00:03 3 浏览 0 评论

接触过数据结构的小伙伴应该都知道程序 = 数据结构 + 算法。数据结构乃组织组织数据的结构,算法就是对这些结构中的数据进行操作,可见数据结构的重要性,就连算法也是依赖于数据结构的。

在博客的开头,我们先简单的聊些数据结构整体的东西。数据结构整体可以分为物理结构和逻辑结构,物理结构指的是数据在磁盘、内存等硬件上的存储结构,主要包括顺序结构和链式结构。而逻辑结构是数据本身所形成的结构,包括集合结构、线性结构树形结构以及图形结构。针对不同的数据结构我们可以依据算法解决不同的问题,如我们在以后博客中要介绍的最小生成树,就是根据算法将带权的连通图转换成最小生成树,在转换这个过程中我们就用到了Prim算法和克鲁斯卡尔算法。当然各种排序算法,最短路径等等也是算法与数据结构的结晶体。

一、线性表综述

本篇博客我们主要介绍的是逻辑结构中的线性表,也就是线性结构。线性结构的特点就好比一串珠子,其特点是第一个节点只有一个后继,没有前驱,最后一个节点是只有一个前驱,没有后继。而其余的节点只有一个前驱和一个后继。说罢了线性表就是一串。下方这个图就是线性表的示例图。中间蓝色的节点前方的是就是改点对应的前驱,后边就是改点对应的后继。从下方可以明确看出head没有前驱只有后继,而tail只有前驱没有后继。

上面这个是线性表的逻辑结构,接下来我们来聊一下线性表的物理结构,也就是存储结构。线性表的物理结构可分为顺序存储结构和链式存储结构。顺序存储结构之所以称之为顺序存储结构因为每个线性表中节点的内存地址是连续的,而链式存储结构中线性表的节点的内存地址可以是不连续的。这也就是在C语言实现顺序存储线性表时先Malloc一块连续的区域,然后用来顺序的存储线性表。而链表中就可以不是连续的了,前驱与后继间的关系由指针连接。

下方这个指示图中,上面这个就是链式存储,下方这个就是顺序存储。可见链式存储是有指针域的,也就是前驱和后继的关系有指针来链接。下方这个链式存储就是单向链表,因为只有前驱到后继的指针,而没有后继到前驱的指针。关于双向链表下方会具体给出详细的说明。而下面第二个图就是顺序存储,前驱与后继的关系是由紧挨的内存地址所关联。

上面这两种存储方式我们可以换另一种更为形象的方式来进行说明,如下所示。顺序存储的内存区块的内存地址是紧挨的,线性关系有相邻的内存地址所保存。当然下方我们是模拟的内存地址,就使用了0x1, 0x2等等来模拟。而链式存储的线性表,在物理存储结构中是不相邻的,仅仅靠内存地址无法去维持这种线性关系。所以就需要一个指针域来指向后继或者前驱节点的内存地址,从而将节点之间进行关联。在单向链式存储中,一个节点不仅仅需要存储数据,而且还要存储该节点下一个节点的内存地址,以便保持这种线性关系。具体请看下图。

原理性质的东西先就聊这么多,下面我们会给出具体实现。主要包括线性表的顺序存储及其操作,以及线性表的单链以及双链存储及其操作。下方的实例依然采用Swift面向对象语言实现,思想理解后,用什么语言都是可以的呢。

二、线性表的顺序存储

关于线性表的顺序存储,我们就使用NSMutableArray来实现,也就是使用OC中的可变数组类型来实现。我们就假设其存储的内存地址是连续的,当然数组中存储对象时要复杂得多,我们暂且假设其中的地址是连续的。下方的list就是我们的顺序线性表,count存储的是已经存入到线性表中的元素个数,而capacity则是记录线性表整个容量的大小。当然上述三个属性都是private的,而下方的计算属性lengthinternal类型的,供外界访问,返回线性表元素的个数。

下方是整体结构,我们下方会给出线性表具体的关键操作,并分析其时间复杂度。

1.往顺序线性表中插入数据

有时候我们会给据特定的算法往线性表中指定的位置插入数据,比如我们常见的插入排序算法,如果你的数据是顺序存储的话,那么就需要将数据插入到顺序表中。接下来我们就将数据插入到指定的位置。

下方该图中是往顺序表中插入一个元素的原理图。在下图中,我们往AB与CD之间插入一个M。在插入M之前我们需要做的事情就是将CD整体往后移动一个位置,为M腾出一个位置,然后再将M这个元素进行插入。

顺序表的插入还是比较简单的,也是非常好理解的,那么用代码实现起来也是用不了几行代码的。下方截图就是是顺序线性表的元素插入的具体实现的代码。首先检查index的合法性,检查index是否在合理范围内,然后将index后的元素依次往后移动,然后将元素插入即可。

2. 顺序线性表的元素移除

上面介绍完元素的插入后,接下来要聊一下元素的移除。也就是移除指定索引中的元素。该过程恰好与上述插入的过程相反,上述在插入之前是相应的元素往后移,腾出index位置。而移除特定索引的元素时,是相应的元素左移,覆盖掉要删除的元素,然后将最后一个元素进行移除掉。下方的原理图对此过程进行了说明。

该部分比较简单,下方的代码段就是将指定索引的元素进行移除。在线性表的顺序存储中,前驱和后继的关系由内存地址的先后顺序所关联,所以插入和删除元素会相对麻烦一些,而查找和修改元素就比较简单了,直接由index可以找到相应的元素,再次就不做过多赘述了。github上所分享的Demo中会有完整示例。

三、线性表的单链存储

介绍完线性表的顺序存储,接下来我们来介绍线性表的链式存储。当然,本部分是对单向链表的介绍,下部分将会对双向链表的介绍。下方截图就是我们单向链表相关示例的运行结果,首先我们先正向的创建链表,也就是后来的元素插入到链表的后方。然后在给出逆向创建链表,与正向创建链表相反,后来的元素掺入到头结点的后方。创建链表完毕后,我们会给出链表元素的插入和移除的解决方案。

1.单向链表的创建

在链表创建之前,我们得先创建节点的类,因为链表是多个节点的连接。下方这个OneDirectionLinkListNote类就是单向链表的节点类。其中的data属性存储的是该节点所存储的数据,而变量next就是指向下一个节点的指针,链表中节点间的关系由next指针所关联。initdeinit就是该类的构造和析构函数了,就不做过多赘述了。

2.链表协议的创建

在创建链表之前,我们可以先创建一个链表协议ListProtocalType。在ListProtocalType协议中定义了链表所必须的方法,无论是单向链表还是双向链表都要遵循该协议。其实这就是“面向接口”的体现。单向链表与双向链表都遵循了这协议,那么说明这两种链表对外的接口是一致的,这样便于程序的维护,这也是面向接口编程的好处。下方就是我们事先定义好的ListProtocalType协议的部分内容。

下方协议中只给出了方法的定义,未给出具体实现。所有链表都要遵循该协议,ListProtocalType中定义了链表结构所必须的方法。可以说下方这个协议就是链表的大纲。

3.单向链表的构建

下方就是我们单向链表类的属性和构造函数。headNote就是我们链表的头结点,而tailNote是我们链表的尾结点。length就是我们链表的长度,也就是我们链表中元素的个数。一个空链表中tailNote = headNote

下方我们将会介绍链表的正向创建和逆向创建。 下方这个截图中就是正向创建链表,其实就是将新创建的数据往链表的尾部插入,然后更新一下tail的位置即可。关键步骤就两步,第一步是tail->next = newNode,第二步是tail = newNode。插入步骤如下所示:

上面这个示意图是往链表的后方添加元素,接下来是往链表的头部插入元素。该过程也是由关键的两步组成,第一步就是newNode->next = head->next,第二步是head->next = newNode。经过这两步我们就可以把元素插入到头结点的后方了。

下方这段代码是正向创建链表的具体代码,就是一直往链表的尾部插入元素。逆向创建链表的代码就不往上粘贴了,与下方代码类似,github上会进行完整实例的分享。上面虽然是往头结点和尾部结点的插入,但是原理是适用于往两边中间插入元素的,在此就不做过多赘述了。

4.单向链表元素的移除

上面我们聊完元素的插入,接下来我们要聊一下元素的移除。要想移除单向链表中的一个元素,首先我们得找到被移除结点的前驱的位置,比如是pre。当前移除的元素是remove,让我我们让pre->next = remove->next, 然后再执行remove->next = nil。经过上面这些步骤,remove这个结点就与链表脱离关系了。示意图如下所示。

根据上述的示意图,我们就可以给出具体的代码实现了。单向链表的核心操作就介绍完了,其他更多细节请参考在github上分享的源代码,因为篇幅有限,关于单向链表的更多细节就不做过多赘述了。

四、双向链表

如果你对单向链表已经理解的话,那么理解双向链表来说并非难事。双向链表与单向链表相比多了一个指向前驱的节点。我们暂且称为将指向前驱的节点命名我pre指针。下方这个示意图就是双向链表的示意图,与单向链表相比,多了一个指向前驱的指针域。如下所示。接下来将会给出双向链表的插入和移除。

1.双向链表元素的插入

双向链表的插入要比单向链表的插入要复杂一些,不过也是蛮好理解的。下方示意图中就是往节点A后方插入一个节点D。主要分为四个步骤,第一步是将D节点的next指针指向A节点next指针指向的节点,也就是D->next = A->next。第二步是将D节点的pre指针指向A节点,也就是D->pre = A。第三步是将A的next指针指向D,也就是A->next = D。最后将D节点的下一个节点的pre指针指向D,也就是D->next->pre = D。经过这几步,我们就可以将节点D插入到A与B的中间。当然这个顺序不是一定的,只要能保证链的正确关联即可。

下方是上述元素插入的核心代码,如下所示。主要将newItem节点,插入到cursor节点后方。

2.双向链表元素的删除

双向链表因为比单向链表多一个前驱指针域,所以元素的删除要麻烦一下,不过还是比较好理解的。下方这个截图就是删除B节点的示意图。首先将B节点前驱节点的next指针域指向B节点的后继,也就是B->pre->next = B->next。 然后将B节点的后继节点的前驱指针指向B的前驱节点,对应着B->next-pre = B->pre。最后将B的next和pre指针域置为nil。如下所示:

下方代码段就是双向链表移除节点的具体实现,如下所示。至于链表的遍历等其他操作在此就不做过多的赘述了,具体内容请看github上分享的源代码。

五、面向接口编程的优点

在上述我们实现两种链表时,我们先定义了一个链表协议ListProtocalType。无论是双向链表还是单向链表都遵循这个协议,也就是说,该协议就是链表对外统一的接口,该协议就是操作链表的一个规范。下方的testLinkedList()就是我们链表的测试方法,该函数的参数是遵循ListProtocalType协议的所有类的对象。也就是说只要遵循了ListProtocalType这个协议的类的对象,都可以作为该函数的参数。至于具体操作,那么不同的类会给出不同的操作的。

在调用该函数时,第一个传入的是单向链表的类的对象,第二个是双向链表的类的对象。虽然都是执行同一个方法,但是因为传入的类的对象不同,所以执行的结果显然是不同的。这也就是利用了面向对象的多态性,在之前设计模式系列的博客中介绍过,下方这种与策略模式类似。

相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码