数组本质
- 数组是线性表数据结构,是一段连续的内存空间,数组存储相同类型的数据
- 论证一下
- 线性表数据结构,这个是定义无必要论证
- 内存空间连续+存储相同类型数据是数组的特点,这两个特点让数组具备了随机访问的特性,即可以通过求址公式计算出任意下标数据存储地址,实现数组的快速查询
数组操作
- 随机访问
- 一维数组求址公式:
a[k]_address = base_address + k * typesize
- 二维数组(m*n)求址公式:
a[i][j] = base_address + (i*n + j) * typesize
- 一维数组求址公式:
- 插入
- 数组插入操作很简单,空间充足情况下直接添加到末尾
- 数组在一开始完成空间申请后,空间的大小固定下来,不支持动态调整。插入元素,在空间不足情况,则插入失败
- 空间不足又无法舍弃情况下,可以进行数组扩容,扩容即重新申请一块更大的空间,并把当前空间中的元素复制过去,扩容操作非常耗时,一般情况下使用数组作为底层数据结构,最好提前预测数据规模,申请到足够的空间,避免不必要的扩容操作。如扩容不可避免,尽可能把复制数据的操作均摊到多次操作中
- 删除
- 删除数组元素需要保证删除后,数组特性不变即数据连续性不变,删除中间元素,排在后面的元素整体前移,同时数组计数相应减少
- 补充1:有序数组插入,需快速找到数组正确插入位置,可以使用二分查找
- 补充2:散列表扩容优化逻辑,当数据量达到一定程度,申请新表,查询操作先查新表,无对应数据,查旧表并将旧表中该数据迁移到新表,实现逐步过渡。需要注意:散列表是无序的,如果是有序数组扩容需要做额外逻辑
- 补充3:数组删除同时要保持数据连续性(假设删除均是指定下标删除操作),这要求在删除一个数据后需要移动其后面的数据填补空缺以保持数据连续性,但这种逻辑时间复杂度较高O(n),需要考虑优化方案。比如先记录要删除数据的位置为delete状态,等到delete状态数据达到一定量级再统一删除(类似java垃圾回收机制)。甚至在空间足够情况下可以不真正删除任何数据,均只做状态标记,这样删除效率为O(1)
数组与容器
- 容器是基于底层数据结构封装实现的,本身提供了很多便捷操作接口,便于开发者调用,一般情况下直接使用即可
- 数组是较底层的数据结构,上层开发中很少用到,除非在为了追求性能的极端的情况下