《数据结构与算法之美》学习笔记@数组

·  阅读 101

计算机基础知识

数组

数组,不仅仅是编程语言中的一种数据类型,还是一种最基础的数据结构。

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

线性表(Linear List

线性表,数据排成像一条线一样的结构。每个线性表上的数据最多只有前、后两个方向。如:

  • 数组
  • 链表
  • 队列

线性表

非线性表

在非线性表中,数据之间并不是简单的前后关系。如:

  • 二叉树

非线性表

连续的内存空间和相同类型的数据

  1. 优点:支持随机访问。
    // 寻址公式:计算出元素存储的内存地址
    a[i]_address = base_address + i * data_type_size
    复制代码
    • base_address 内存块的首地址。
    • data_type_size 数组中每个元素的大小。
    • i 下标(偏移 offset)。从 0 开始编号,当随机访问数组元素时 CPU 避免一次减法运算
  2. 缺点:删除、插入低效(需要做大量的数据搬移工作来保证连续性)。

链表适合插入、删除,时间复杂度 O(1) ;数组适合查找(支持随机访问),根据下标随机访问的时间复杂度为 O(1)

低效的插入操作

设数组的长度为 n,需要在第 k 个位置插入一个数据。

  1. 数组中的数据是有序的:为了把第 k 个位置腾出来给新来的数据,需要将第 k ~ n 这部分的元素顺序地往后挪一位。

    场景细分简单分析时间复杂度
    最佳情况:在数组的末尾插入元素不需要移动数据O(1)
    最坏情况:在数组的开头插入元素所有的数据都需要依次往后移动一位O(n)
    平均情况每个位置插入元素的概率是一样的O(n)
  2. 数组中的数据是无序的:直接将第 k 位的数据搬移到数组元素的最后,把新的元素直接放入第 k 个位置。

    利用这种处理技巧,在特定场景下,在第 k 个位置插入一个元素的时间复杂度就会将为 O(1)

低效的删除操作

设数组的长度为 n,需要将第 k 个位置的数据删除。

和插入类似。在某些特殊场景不一定非得追求数组中数据的连续性时,先标记要删除的元素,然后将多次删除操作集中在一起执行

警惕数组的访问越界问题

访问数组的本质是访问一段连续内存,只要数组通过偏移计算(寻址公式)得到内存地址是可用的,那么程序就可能不会报任何错误。这种情况下,一般都会出现莫名其妙的逻辑错误

容器 vs. 数组

针对数组类型,很多语言都提供了容器类。如:Java 中的 ArrayList

  • 可以将很多数组操作的细节封装起来;
  • 支持动态扩容,无需关心底层的扩容逻辑。

扩容 涉及内存申请和数据搬移,比较耗时,建议事先指定数据大小(节省多次内存申请和数据搬移操作)。

因为需要分配连续的内存空间,数组本身在定义的时候需要预先指定大小。否则需要重新分配一块更大的空间,将原来的数据复制过去,然后再将新的数据插入。

更适合用数组的场景

  1. 特别关注性能,或需要适用基本类型;
  2. 数据大小事先已知,且对数据的操作非常简单;
  3. 多维数组。

小结

  • 业务开发,容器即可(省时省力);
  • 底层开发,性能优化需要做到极致,数组优先。
分类:
代码人生
标签:
分类:
代码人生
标签: