数组是一种线性数据结构,由相同数据类型的元素按顺序存储在连续的内存空间中。它通过索引(Index) 访问元素,索引从 0
开始(部分编程语言如 Lua 从 1
开始,但主流语言如 C、Java、Python 均从 0
开始)。
2.1 连续内存存储 数组元素在内存中占用连续的地址空间,可通过公式快速计算元素地址:
地址 =基地址+索引*单个元素大小
这使得数组支持随机访问,即通过索引直接访问元素的时间复杂度为 O(1)。
2.2 类型一致性 数组中所有元素的数据类型必须相同(静态类型语言如 C、Java 严格限制,动态类型语言如 Python 可存储任意对象,但本质上仍按统一类型管理内存)。
2.3固定大小与动态扩展
静态数组:创建时大小固定(如 C 语言的 int arr[5]
),无法动态调整,可能导致内存浪费或溢出。
动态数组:通过扩容机制(如 Java 的 ArrayList
、Python 的列表)动态调整大小,底层仍基于连续内存,但支持自动扩容(通常通过复制原数组到更大的新数组实现)。
3.1按维度分类
一维数组:最简单的线性结构,如 [1, 2, 3, 4]。
多维数组:
三维及以上数组:用于存储多维数据(如图像像素、三维网格等),但实际开发中较少使用,复杂度较高。
二维数组:类似表格或矩阵,如 int[][] matrix = {{1,2}, {3,4}}
,可理解为 “数组的数组”。
按内存分配方式分类
静态数组:内存由编译器在栈或全局区分配,大小固定(如 C 语言的栈数组)。动态数组:内存由程序员在堆区手动分配(如 C 语言的 malloc
)或由语言 runtime 管理(如 Python 列表),支持大小调整。
以下操作以一维数组为例,时间复杂度基于最坏情况分析。
4.1访问元素(Get)
操作:通过索引直接获取元素
时间复杂度:O(1)
示例(Python):
arr = [10, 20, 30]
print(arr[1]) # 输出 20
4.2更新元素(Set)
操作:通过索引修改指定位置的元素。
时间复杂度:O(1)
示例(Python):
arr[1] = 25 # 数组变为 [10, 25, 30]
4.3插入元素(Insert)
操作:在中间 / 开头插入:从插入位置开始,将后续元素依次后移一位,腾出空间插入新元素。在数组末尾插入:直接添加(动态数组需检查容量,可能触发扩容)。
时间复杂度:
中间 / 开头插入:O(n)(需移动 n-i
个元素,i
为插入位置)。
末尾插入:O(1)(平均,不考虑扩容)。
示例(插入到索引 1 处): 原数组:[1, 3, 4]
→ 插入 2
后:[1, 2, 3, 4]
(索引 1 及之后的元素后移)。
4.4删除元素(Delete)
操作:删除中间 / 开头元素:将删除位置之后的元素依次前移一位,覆盖被删除元素。删除末尾元素:直接移除(无需移动元素)。
时间复杂度:
中间 / 开头删除:O(n)(需移动 n-i-1
个元素)。
末尾删除:O(1)。示例(删除索引 1 处的元素): 原数组:[1, 2, 3, 4]
→ 删除后:[1, 3, 4]
(索引 1 之后的元素前移)。
4.5查找元素(Search)
顺序查找:遍历数组寻找目标元素,时间复杂度 O(n)。
二分查找:仅适用于有序数组,通过不断将数组折半缩小范围,时间复杂度 O(log n)。示例(二分查找): 有序数组 [1, 3, 5, 7, 9]
,查找 5
的索引为 2。
4.6其他操作
遍历数组:依次访问每个元素,时间复杂度 O(n)。
排序数组:如冒泡排序、快速排序等,时间复杂度通常为 O(n log n)(优化算法)。
优点 | 缺点 |
---|---|
1. 随机访问效率极高(O (1))。 | 1. 插入 / 删除中间元素需移动大量元素(O (n))。 |
2. 内存空间紧凑,存储效率高。 | 2. 静态数组大小固定,灵活性低。 |
3. 适用于实现其他数据结构(如堆、哈希表)。 | 3. 动态数组扩容时可能产生额外开销(复制元素)。 |
4. 支持多维数据结构(如矩阵)。 | 4. 内存必须连续,可能因内存碎片无法分配大块空间。 |
6.1 基础数据存储 如存储学生成绩、用户列表等线性数据,利用索引快速访问。
6.2 高性能场景
缓冲区(Buffer):如网络数据收发、文件读写时的临时存储,利用连续内存提高 I/O 效率。
科学计算:多维数组用于矩阵运算、图像像素存储(如二维数组表示灰度图像)。
6.3数据结构底层实现
堆(Heap):用一维数组存储完全二叉树,通过索引计算父子节点位置。
哈希表(Hash Table):开放寻址法用数组存储键值对,解决哈希冲突。
栈和队列:动态数组可实现顺序栈或循环队列(通过指针标记头尾)。
6.4编程语言内置类型
C/Java:静态数组 int[] arr = new int[5];
。Python/JavaScript:列表(List)本质是动态数组,支持灵活操作。
数据结构 | 内存存储 | 随机访问 | 插入 / 删除效率 | 典型应用 |
---|---|---|---|---|
数组 | 连续 | O(1) | O (n)(中间) | 缓冲区、堆、矩阵 |
链表 | 非连续 | O(n) | O (1)(已知节点) | 频繁插入删除场景 |
栈 / 队列 | 基于数组或链表 | 受限 | O (1)(端点操作) | 函数调用栈、任务队列 |
8.1 索引越界:访问超过数组长度的索引会导致非法内存访问(如 C 语言崩溃,Python 抛出 IndexError
)。
8.2 动态数组扩容策略:常见策略为翻倍扩容(如容量不足时新容量为原容量的 2 倍),均摊时间复杂度接近 O (1)。
8.3 多维数组的内存布局:二维数组在内存中按行优先(Row-major)或列优先(Column-major)存储(如 C 语言按行优先)。
数组是最基础的数据结构之一,其连续存储和随机访问特性使其在需要高效查询的场景中表现优异,但插入 / 删除操作效率较低。理解数组的底层原理(如内存分配、索引计算)是掌握其他复杂数据结构(如链表、树)的基础。在实际开发中,需根据场景选择静态数组或动态数组,或结合其他数据结构(如链表 + 数组实现跳表)优化性能。