Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

数组

复习

  • 输入规模与增长速度:关注算法代价随规模的增长趋势
  • 时间与空间的交换:用空间常常能换时间
  • 抽象数据类型:先规定能做什么,再决定怎么做

TL;DR

  • 数组在内存中连续存放,每个元素大小相同
  • 连续带来了随机访问:按下标一步定位
  • 代价是插入和删除往往要移动大量元素
  • “查询快、增删慢”,是数组最典型的取舍

正文

  前面几章都在讲“怎样衡量”,现在可以开始认识具体的数据结构了。最基础、也最常见的一个,就是数组(array)。

一排编号的格子

  想象一排连续排列的储物格,每个格子一样大,并且从 0 开始编号。数组就长这样:它在内存中占据一段连续的空间,元素一个挨一个地放着。

  因为每个元素大小相同、位置又连续,只要知道两件事,就能算出任何一个元素的地址:

元素地址 = 起始地址 + 下标 × 单个元素大小

  注意,这是一步就能算出来的,不需要从头找起。所以数组按下标访问任意元素,代价都是固定的——这就是随机访问(random access)。它很快,是数组最大的优点。

快在哪,慢在哪

  可也正因为“连续”,数组在中间插入或删除元素时就很麻烦。

  比如要在第 3 个位置插入一个新元素。为了给它腾出位置,从第 3 个开始的所有元素,都得整体往后挪一格。删除则相反,要把后面的元素往前挪。数据一多,挪动量就很大,代价是 O(n)

  于是数组的性格非常鲜明:

  • 按下标访问O(1),极快
  • 在中间插入或删除O(n),较慢

  这又一次印证了那句老话:没有一种结构能让所有操作都最优。 数组用“连续”换来了随机访问,也为此付出了“增删要挪动”的代价。

  另外,普通数组的容量是固定的:创建时就定好了能放多少个。那如果一开始不知道要放多少数据,怎么办?下一章来解决。

思考题 1

  数组为什么能按下标“一步”访问任意元素?

思考题 2

  在数组中间插入一个元素,为什么代价是 O(n)

小结

知识点

  • 数组在内存中连续存放,元素等大
  • 连续使随机访问成为可能,代价 O(1)
  • 中间插入与删除需移动元素,代价 O(n)
  • 普通数组容量固定

参考资料

  1. Wikipedia(zh):数组:连续存储的同类元素序列
  2. Wikipedia(zh):随机存取:可直接访问任意位置的数据访问方式

思考题答案(仅供参考)

思考题 1

  因为元素连续且等大,地址可以用“起始地址 + 下标 × 元素大小”一步算出,不必逐个查找,所以任意下标都能直接定位,代价固定。

思考题 2

  因为要维持“连续”这个性质,插入位置之后的所有元素都必须整体后移一格,腾出空位。移动的元素数量与规模成正比,所以是 O(n)

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪