算法 | 对数组应用的学习

333 阅读2分钟

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

数组定义

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

如何实现随机访问?

  • 线性表,用连续的存储空间存储相同类型数据。
  • 寻址表达式: arr[i]_address = base_address + i*data_type_size
  • 复杂度分析:
    • 随机访问:O(1)
    • 二分查找:O(logn)

低效的插入&删除

为了保持内存数据的连续性,导致插入、删除操作比较低效。

  • 复杂度分析:
    • 最好-末尾:O(1)
    • 最坏-开头:O(n)
    • 平均复杂度:O(n)
  • 特殊插入:如果数组不要求数据有序,可通过插入是先取出插入位置元素放到队尾,插入元素,替换插入位置元素,则复杂度为O(1)
  • 标记删除: 先将要删除元素标记删除并不实际删除,未删除元素也不清理,直到内存不足或触发删除条件是,再清理数据,重排元素(参考JVM的标记清除垃圾回收算法)。

数组容器-ArrayList

优点

  • 封装数组操作细节,比如插入、删除。
  • 支持动态扩容,比如,每次存储空间不够时,会将空间自动扩容1.5倍大小。
  • 动态扩容涉及内存申请和数据迁移,如若确定最好事先指定数据大小。

缺点

  • Java ArrayList无法存储基础数据类型,比如int/long,需要封为Integer/Long类,Autoboxing/Unboxing需要一定性能损耗。
  • 如果大小事先已知,并对数据操作简单,则用不到容器提供功能,也可直接数组。

数组与容器选择,底层开发,性能追求极致,数组优于容器;否则推荐容器。

思考:

  1. 前面我基于数组的原理引出 JVM 的标记清除垃圾回收算法的核心理念。我不知道你是否使用 Java 语言,理解 JVM,如果你熟悉,可以在评论区回顾下你理解的标记清除垃圾回收算法。
  2. 前面我们讲到一维数组的内存寻址公式,那你可以思考一下,类比一下,二维数组的内存寻址公式是怎样的呢? 问题一:
    标记清除算法,核心标记、清除两阶段,先标记处需回收对象,最后再统一触发回收清除,减少频繁的GC阻塞。
    问题二:
    二维数组的寻址表达式: object arr[][] = new object[n][m] arr[i][j]_address = base_address+(i*n+j)*data_type_size