数据结构-数组的介绍

数据结构之数组(Array)详解

一、数组的概念

数组是一种线性数据结构,由相同数据类型的元素顺序存储在连续的内存空间中。它通过索引(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 语言按行优先)。

九、总结

数组是最基础的数据结构之一,其连续存储随机访问特性使其在需要高效查询的场景中表现优异,但插入 / 删除操作效率较低。理解数组的底层原理(如内存分配、索引计算)是掌握其他复杂数据结构(如链表、树)的基础。在实际开发中,需根据场景选择静态数组或动态数组,或结合其他数据结构(如链表 + 数组实现跳表)优化性能。

你可能感兴趣的:(数据结构,python,算法,java,数据结构)