05 | 数组:为什么很多编程语言中数组都从0开始编号?

86 阅读2分钟

如何实现随机访问

  • 什么是数组

    • 线性表数据结构

      • 线性表数据像一条线 
      • 线性表(最多只有前和后两个方向):数组,链表、队列、栈等等
      • 非线性表(并不是简单的前后关系):二叉树、堆、图等
    • 连续的内存空间,存储一组相同类型的数据

  • 数组的特点

    • 线性表

    • 连续的内存空间和相同类型的数据 (是随机访问的前提条件,也是大量数据搬移的主要原因)

      • 内存地址的访问公式:a[i]_address = base_address + i * data_type_size
    • 数组在随机访问的情况下时间复杂度为O(1)

低效的“插入”和“删除”

  • 插入操作

    • 平均时间复杂度o(n)

      • 第一个位置是o(1)
      • 最后一个位置是o(n)
      • 平均情况时间复杂度为 (1+2+...n)/n=O(n)\
    • 每次都需要移动n-k个元素

    • 优化手段:不需要有序的场景下直接和空数据swap即可

  • 删除操作

    • 平均时间复杂度o(n)
    • 优化:懒删除 每次记录删除下标,当存储不够的时候真正执行删除操作 JVM标记清除垃圾回收算法的核心思想

警惕数组的访问越界问题

  • c语言中不会校验数据越界

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

  • 利用数组越界进行非法攻击

容器能否完全替代数组?

  • 容器的优点:

    • 封装数据操作细节
    • 支持动态扩容,将不够用的数组复制大更大的数组中
    • 可以在初始化容器时指定容器大小
  • 特点

    • Java ArrayList 无法存储基本类型 Autoboxing、Unboxing 有一定的性能消耗\

    • 数据大小事先已知则可用数组\

    • 多维数组比多维list更直观\

  • 业务开发使用list就够了,没必要为了一点性能而使用数组

  • 当性能是第一标准时,才真正使用数组

解答开篇

  • 下标”最确切的定义应该是“偏移(offset)\

  • a[k]_address = base_address + k * type_size

  • 如果起始位置从1开始则是a[k]_address = base_address + (k-1)*type_size  需要多执行一次减法指令

总结

  • 数组

    • 用一块连续的内存空间,来存储相同类型的一组数据
    • 随机访问o(1)
    • 追求极致性能可以用数组

\