数据结构-数组

一、什么是数组

数组(Array)是一种线性表数据结构,它用一组连续的内存空间来存储一组具有相同类型的数据。

下面是两个值得注意的概念:

1. 线性表(linear list)

线性表,即数据的逻辑结构是线性的。每个线性表中的数据最多只有向前和向后两个方向。典型的线性表有数组、链表、队列和栈。

和线性表相对应的就是非线性表,即数据之间不是简单的前后关系。如树、堆、图等。

2. 连续的内存空间和相同类型的数据

这使数组具有一个堪称“杀手锏”的特性 ——“随机访问”,即可以通过下标随机访问数组元素。我们知道,计算机会给每个内存单元分配一个地址,然后通过地址来访问内存中的数据。当计算机需要访问数组中的某个元素时,就会通过下面的寻址公式,计算出存储该元素的内存地址:

a[i]_address = base_address + i * data_type_size

其中,data_type_size 是数组中每个元素的大小,base_address 是内存块的首地址。这样,就可以用 O(1) 的时间复杂度获取到下标为 i 的数组元素。

当然,有利就有弊,这一特性使得数组的很多操作变得低效,比如插入、删除操作,为了保证连续性,需要做大量的数据搬移工作。

二、数组的特性

数组为了保持内存数据的连续性,会导致插入、删除这两个操作比较低效。现在我们就来详细说一下,究竟为什么会导致低效?又有哪些改进方法呢?

1. 插入

假设数组长度为 n,要在第 k 位插入一个元素。那么就需要将第 k ~ n 位元素顺序后移,然后将新元素插入第 k 位。

如果 k = n - 1,则不需要再移动数据,此时时间复杂度为 O(1);如果 k = 0,则需要移动所有的数据,此时时间复杂度为 O(n)。用前一篇文章的分析方法( 最好、最坏、平均、均摊时间复杂度分析),可知平均时间复杂度为:

(1 + 2 + ... + n) / n = O(n)

所以,数组的插入操作平均时间复杂度为 O(n),是一项低效的操作。

但是,如果数组中存储的数据没有任何规律,插入后不需要保持原来的顺序,则可以进行如下操作:

直接将第 k 位的数据搬移到数组元素最后,然后把新的元素直接放在第 k 位。

利用这种处理技巧,在特定场景下,在第 k 个位置插入一个元素的时间复杂度就会降为 O(1)。这个处理思想在快排中也会用到,我会在排序那一节具体来讲,这里就说到这儿。

2. 删除

和插入操作类似,如果要删除第 k 位的元素,为了内存的连续性,也需要搬移数据,否则中间就会出现空洞,内存就不连续了。容易分析出,删除操作的平均时间复杂度也为 O(n)。

事实上,我们可以将多次删除操作集中在一起执行,先标记下已经删除的数据,然后在某些条件下(比如没有更多空间存储数据时),统一执行真正的删除操作,这样就大大减少了删除操作导致的数据搬移。

这种标记清除思想在其他地方也有所应用,比如垃圾回收中的标记清除算法。JVM垃圾回收算法及收集器

所以数据结构和算法的魅力就在于此,很多时候我们并不是要去死记硬背某个数据结构或者算法,而是要学习它背后的思想和处理技巧,这些东西才是最有价值的

3.警惕数组的访问越界问题

访问数组的本质是访问一段连续内存,只要数组通过偏移计算得到的内存地址都是可用的,那么程序就可能不会报任何错误。

很多计算机病毒也正是利用了代码中数组越界可以访问非法地址的漏洞来进行攻击,所以写代码时一定要警惕数组越界。

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haoxin963

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值