数组与链表——异同分析

       近期开始看《图解算法》,虽然很早以前就接触过算法这块的,但还是不得不惊叹作者的功力,巧妙地借助漫画的形式和相关的例子把复杂的原理讲得如此有趣和透彻。这里将先跳过大O(时间复杂度、空间复杂度)相关部分的介绍,以及关于最佳、平均、最坏情况下复杂度的估计方法。


内存

首先是介绍内存的概念,一般来说,数据都是经硬盘加载到内存后才可以进行后续处理(把硬盘空间大小当成内存空间大小的请面壁思过…)。那么数据在内存中总得一个存储方式吧,大致如下图所示。可以将一个格子当做一个房间,一个房间里可以进一个人(1byte),那么总共有2^{64}个房间可以住。用稍微专业点话来讲就是:对于64位计算机,理论上内存地址可用从0x00000000开始,一直到0xFFFFFFFF结束,而一个地址中有可以存储一个1byte,总共有2^{64}个byte,约1.6\times 10^{7}Tb(够大了吧,够用了吧)。


那么数据在内存中又是如何存放的呢?这里不得不介绍两个概念:顺序存储和随机存储,主要介绍将通过数组(代表顺序存储)和链表(代表随机存储)进行展开。

数组

数组的存储方式为顺序存储,简单的来说,就是系统为待存储内容在内存中寻找一块连续的空间(中间没有跳跃),并将其存入。

例如,我们要将一串字符“abcdefg”存放到内存中,则存储的方式可以如下所示。其中数据是连续存放,中间未出现过隔断。那要访问的话又是如何进行呢?如访问数组的第四个元素,可以这么写a[3]。'a'的话则是a[0]。

**(可跳过这段)而为什么索引是从0而不是从1开始,这个就与计算地址偏移量的方式有关。用c语言的方式进行访问:*(a+0)、*(a+3),其表示的是按照偏移量进行数据读取的方法。

那么事情来了,当我们要在'd'后后面加入两个新字符"ee",这改怎么办呢?此时我们尴尬的发现,虽然仅仅是多了两个字符,但原先8个byte的大小以及存放不下新的数据了!那么我们只好释放这块内存,并在内存中重新寻找一块新的连续空间进行存储。换句话说,当我们和酒店说要预定一层所有的10个房间,但是这一层只要有一个房间在使用,我们就无法进行预定,只能换一层继续寻找。而且当我们因其他因素需多预定2个房间时,因为这个酒店一层最多只有10个房间,不得不寻找新的合适的酒店。

删除方法与插入的基本一致,这里就不再描述。

数组的总结如下:可以进行随机读取,但在写入时必须按顺序存放

 数组
读取O(1)
插入O(n)

链表

介绍完数据,那么另一位大将也来了,就是链表,它是随机存储方式的代表,即存储时可以对数据进行拆分,并在内存的任意位置进行存放。

接上文数据的事情,当我们需要存储字符串“abcdefg”时,又该怎么办呢?图中阴影部分表示空间被占用,因此我们的数据可以跳跃着前进,在空余的部分将数据存入。那么将如何进行访问呢?首先我们要找到起点‘a’的地址,并按照箭头的指示方向依次进行前进。因为随机存储的方式决定了数据的存放方式,即数据是被存放在内存中的不同位置,无法通过计算地址偏移量来获取数据。

经过一番功夫,这个数据是存到内存里了。和数组部分中介绍的那样,当我们想在字符'd'后加两个字符“ee”时,又该怎么做呢?

此时,我们无需对上图内容存储的方式进行改变,只需在内存再找两个未使用的地址,并改变字符'e'对下一节点的指向即可,如图所示(请无视图中歪歪扭扭的曲线…)。

删除方法与插入的基本一致,这里就不再描述了。

 链表的总结如下:可以进行随机插入,但在读取时必须顺序查询。

 链表
读取O(n)
插入O(1)

总结时刻:

数组和链表都是数据存储的一种手段,但两者的存储方式,致使各自在读取、插入、删除、查询时的效率各有优劣,因而需要视具体情况选择合适的存储结构。

 数组链表
读取O(1)O(n)
插入O(n)O(1)
删除O(n)O(1)

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值