【03】数组
1. 什么是数组?
数组(Array)是一种线性表数据结构,它用一组连续的内存空间,来存储相同类型的数据。
2. 什么是线性表(Linear List)?
线性表就是数据排成一条线一样的结构,每个线性表的数据最多只有前后两个方向。
例如:数组,链表,队列,栈 等都是线性表结构
3. 什么是非线性表?
例如:二叉树,堆,图,等,是非线性表,是因为,在非线性表中,数据之间并不是简单的前后关系
4. 为什么很多编程语言的数组都是从0开始编号的?
- 从数组存储的内存模型上来看,“下标”确切的说法就是一种“偏移”,相比从1开始编号,从0开始编号会少一次减法运算,数组作为非常基础的数组结构,通过下标随机访问元素又是非常基础的操作,效率的优化就要尽可能的做到极致。
- 主要的原因是历史原因,C语言的设计者是从0开始计数数组下标的,之后的Java、JS等语言都进行了效仿,或者说是为了减少从C转向Java、JS等的学习成本。
5. 连续的内存空间和相同类型的数据的优缺点
优点:两限制使得具有随机访问的特性
缺点:删除,插入数据效率低
6. 数组怎么根据下标随机访问的?
通过寻址公式,计算出该元素存储的内存地址:
a[i]_address = base_address + i * data_type_size
例如:int[] a = new int[10]
- 计算机给数组a[10],分配了一组连续的内存空间
- 比如内存块的首地址为
base_address = 1000
- 当计算给每个内存单元分配一个地址,计算机通过地址来访问数据。当计算机需要访问数组的某个元素的时候,会通过一个寻址公式来计算存储的内存地址。
公式如下:
a[i]_address = base_address + i * data_type_size
arr[i] 首地址 = 数组内存块首地址 + 数据类型大小 * i ,其中i为偏移量。
base_address:内存块的首地址
data_type_size:数组中每个元素的大小,比如每个元素大小是4个字节。
7. 为何数组插入和删除低效?
7.1 数组插入
若有一元素想往int[n]的第k个位置插入数据,需要在n-k的位置往后移。
最好情况时间复杂度 O(1)
最坏情况复杂度为O(n)
平均复杂度为O(n)
如果数组中的数据不是有序的,也就是无规律的情况下,可以直接把第k个位置上的数据移到最后,然后将插入的数据直接放在第k个位置上。
这样时间复杂度就将为 O(1)了。
7.2 数组删除
与插入类似,为了保持内存的连续性。
最好情况时间复杂度 O(1)
最坏情况复杂度为O(n)
平均复杂度为O(n)
提高效率方法:将多次删除操作中集中在一起执行,可以先记录已经删除的数据,但是不进行数据迁移,而仅仅是记录,当发现没有更多空间存储时,再执行真正的删除操作。这也是 JVM 标记清除垃圾回收算法的核心思想。
8. 数组访问越界问题
C语言中的数据越界是一种未决行为,一般比较难发现的逻辑错误。相比之下,Java会有越界检查。用C语言循环越界访问的例子说明访问越界的bug。此例在《C陷阱与缺陷》出现过,很惭愧,看过但是现在也只有一丢丢印象。翻了下书,替作者加上一句话:如果用来编译这段程序的编译器按照内存地址递减的方式给变量分配内存,那么内存中的i将会被置为0,则为死循环永远出不去。
9. 数组和链表的面试纠错?
- 数组中的元素存在一个连续的内存空间中,而链表中的元素可以不存在于连续的内存空间。
- 数组支持随机访问,根据下标随机访问的时间复杂度是O(1);链表适合插入、删除操作,时间复杂度为O(1)。
10. 容器是否完全替代数组?
相比于数组,容器的优势:对于Java语言,容器封装了数组插入、删除等操作的细节,并且支持动态扩容。一旦超过存储容量,扩容时比较耗内存,因为涉及到内存申请和数据搬移。
对于Java,一些更适合用数组的场景:
- Java的ArrayList无法存储基本类型,需要进行装箱操作,而装箱与拆箱操作都会有一定的性能消耗,如果特别注意性能,或者希望使用基本类型,就可以选用数组。
- 若数据大小事先已知,并且涉及的数据操作非常简单,不需要使用到ArrayList提供的大部分方法,则可以直接使用数组。
- 表示多维数组时,数组往往更加直观。
- 业务开发容器即可,底层开发,如网络框架,性能优化。选择数组。
11. JVM标记清除算法?
GC最基础的收集算法就是标记-清除算法,如同他们的名字一样,此算法分为“标记”、“清除”两个阶段,先标记出需要回收的对象,再统一回收标记的对象。不足有二,一是效率不高,二是产生碎片内存空间。
12. 数组的内存寻址公式?
一维数组:a[i]_address=base_address+i*type_size
二维数组:二维数组假设是mn, a[i][j]_address=base_address + (i*n+j)*type_size
三维数组:三维数组假设是mn*q, a[i][j][k]_address=base_address + (i*n*q + j*q + k)*type_size
13. 参考资料
- 王争老师在极客时间的专栏《数据结构与算法之美》
- 专栏下的所有评论
14. 声明
本文章是学习王争老师在极客时间专栏——《数据结构与算法之美》的学习总结,文章很多内容直接引用了专栏下的回复,推荐大家购买王争老师的专栏进行更加详细的学习
。本文仅供学习使用,勿作他用,如侵犯权益,请联系我,立即删除。