计算机基础知识
数组
数组,不仅仅是编程语言中的一种数据类型,还是一种最基础的数据结构。
数组(Array)是一种线性表数据结构。它用一组连续的内存空间来存储一组具有相同类型的数据。
线性表(Linear List)
线性表,数据排成像一条线一样的结构。每个线性表上的数据最多只有前、后两个方向。如:
- 数组
- 链表
- 队列
- 栈
非线性表
在非线性表中,数据之间并不是简单的前后关系。如:
- 二叉树
- 堆
- 图
连续的内存空间和相同类型的数据
- 优点:支持随机访问。
// 寻址公式:计算出元素存储的内存地址 a[i]_address = base_address + i * data_type_sizebase_address内存块的首地址。data_type_size数组中每个元素的大小。i下标(偏移offset)。从 0 开始编号,当随机访问数组元素时CPU避免一次减法运算。
- 缺点:删除、插入低效(需要做大量的数据搬移工作来保证连续性)。
链表适合插入、删除,时间复杂度 O(1) ;数组适合查找(支持随机访问),根据下标随机访问的时间复杂度为 O(1) 。
低效的插入操作
设数组的长度为 n,需要在第 k 个位置插入一个数据。
-
数组中的数据是有序的:为了把第
k个位置腾出来给新来的数据,需要将第k~n这部分的元素顺序地往后挪一位。场景细分 简单分析 时间复杂度 最佳情况:在数组的末尾插入元素 不需要移动数据 O(1)最坏情况:在数组的开头插入元素 所有的数据都需要依次往后移动一位 O(n)平均情况 每个位置插入元素的概率是一样的 O(n) -
数组中的数据是无序的:直接将第
k位的数据搬移到数组元素的最后,把新的元素直接放入第k个位置。利用这种处理技巧,在特定场景下,在第
k个位置插入一个元素的时间复杂度就会将为O(1)。
低效的删除操作
设数组的长度为 n,需要将第 k 个位置的数据删除。
和插入类似。在某些特殊场景不一定非得追求数组中数据的连续性时,先标记要删除的元素,然后将多次删除操作集中在一起执行。
警惕数组的访问越界问题
访问数组的本质是访问一段连续内存,只要数组通过偏移计算(寻址公式)得到内存地址是可用的,那么程序就可能不会报任何错误。这种情况下,一般都会出现莫名其妙的逻辑错误。
容器 vs. 数组
针对数组类型,很多语言都提供了容器类。如:Java 中的 ArrayList 。
- 可以将很多数组操作的细节封装起来;
- 支持动态扩容,无需关心底层的扩容逻辑。
扩容 涉及内存申请和数据搬移,比较耗时,建议事先指定数据大小(节省多次内存申请和数据搬移操作)。
因为需要分配连续的内存空间,数组本身在定义的时候需要预先指定大小。否则需要重新分配一块更大的空间,将原来的数据复制过去,然后再将新的数据插入。
更适合用数组的场景
- 特别关注性能,或需要适用基本类型;
- 数据大小事先已知,且对数据的操作非常简单;
- 多维数组。
小结
- 业务开发,容器即可(省时省力);
- 底层开发,性能优化需要做到极致,数组优先。