在数据结构的世界里数组Array是最基础、最古老、也是应用最广泛的结构之一。它就像一个整齐划一的公寓楼——每个房间编号连续大小相同通过门牌号索引可以瞬间找到任何一间房间。正是这种连续内存和随机访问的特性使数组成为几乎所有高级数据结构的底层构建模块。本文将从数组的定义、内存模型、基本操作、动态数组的实现、多维数组、复杂度分析以及典型应用等方面带你全面理解数组。一、什么是数组数组是一种线性数据结构它用一组连续的内存空间存储相同类型的数据元素并通过索引下标来访问每个元素。在大多数编程语言中数组的索引从 0 开始。数组的核心特点可以概括为连续内存所有元素在内存中紧密排列没有空隙。固定类型数组中的元素通常具有相同的数据类型在静态类型语言中强制在动态语言如 Python 中虽可混合但底层仍是同质。随机访问通过索引可以在 O(1) 时间内直接访问任意元素。静态或动态有的语言提供静态数组长度固定有的提供动态数组可自动扩容。二、数组的内存模型理解数组的关键是理解它的内存布局。假设我们声明一个整型数组int arr[5]内存模型如下地址元素0x1000arr[0]0x1004arr[1]0x1008arr[2]0x100Carr[3]0x1010arr[4]由于每个int占 4 字节在大多数系统上相邻元素的内存地址相差 4 字节。当我们写arr[i]时编译器会计算实际地址base_address i * element_size从而直接定位到目标元素无需遍历。这种“公式计算”就是数组 O(1) 随机访问的本质。三、数组的基本操作数组支持的操作相对简单但不同操作的时间复杂度差异巨大。1. 访问Access给定索引 i直接获取元素值。时间复杂度O(1)。# Python 列表作为动态数组 arr [10, 20, 30, 40, 50] element arr[2] # 302. 搜索Search查找某个值是否存在通常需要遍历数组。时间复杂度O(n)无序情况下。def search(arr, target): for i, val in enumerate(arr): if val target: return i return -13. 插入Insert在末尾插入通常 O(1)动态数组需要考虑扩容。在中间/开头插入需要移动后续元素平均 O(n)。# Python 列表的 insert 操作 arr [1, 2, 3, 4, 5] arr.insert(2, 99) # 在索引 2 处插入 99后面的元素右移 print(arr) # [1, 2, 99, 3, 4, 5]4. 删除Delete删除末尾元素O(1)。删除中间/开头元素需要移动后续元素填补空缺平均 O(n)。# 删除索引 2 的元素 del arr[2] # 后面元素左移5. 更新Update通过索引赋值O(1)。arr[2] 100四、数组的实现静态数组与动态数组1. 静态数组Static Array长度在声明时确定之后不可改变。C 语言中的原生数组是典型代表。int arr[10]; // 固定容量为 10优点内存分配一次完成无扩容开销。缺点容量固定可能浪费空间或不够用。2. 动态数组Dynamic Array可以自动扩容的数组在 Python 中就是list在 Java 中是ArrayList在 C 中是vector。动态数组的实现原理底层使用静态数组存储元素。维护一个容量capacity和一个实际大小size。当 size 达到 capacity 时创建一块更大的新数组通常扩容为原容量的 1.5 倍或 2 倍复制旧元素然后释放旧数组。Python 列表的简化实现示例class DynamicArray: def __init__(self, capacity10): self._data [None] * capacity self._capacity capacity self._size 0 def append(self, item): if self._size self._capacity: self._resize(2 * self._capacity) # 扩容为 2 倍 self._data[self._size] item self._size 1 def insert(self, index, item): if self._size self._capacity: self._resize(2 * self._capacity) # 将 index 及之后的元素右移 for i in range(self._size, index, -1): self._data[i] self._data[i-1] self._data[index] item self._size 1 def pop(self): if self._size 0: raise IndexError(pop from empty array) item self._data[self._size - 1] self._size - 1 # 可选缩容避免浪费过多空间 if self._size self._capacity // 4: self._resize(self._capacity // 2) return item def _resize(self, new_capacity): new_data [None] * new_capacity for i in range(self._size): new_data[i] self._data[i] self._data new_data self._capacity new_capacity def __getitem__(self, index): if index 0 or index self._size: raise IndexError(index out of range) return self._data[index] def __setitem__(self, index, value): if index 0 or index self._size: raise IndexError(index out of range) self._data[index] value def __len__(self): return self._size扩容策略通常采用倍数扩容如 2 倍使得均摊时间复杂度为 O(1)。如果每次只增加固定大小会导致频繁扩容性能下降。五、多维数组数组可以有多维形式最常见的是二维数组矩阵。在内存中多维数组通常以行优先Row-major或列优先Column-major的方式存储。# Python 中使用嵌套列表表示二维数组 matrix [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] # 访问第二行第三列索引从0开始 element matrix[1][2] # 6二维数组的随机访问依然 O(1)matrix[i][j]实际地址 base (i * cols j) * elem_size行优先。多维数组在图像处理、科学计算、机器学习等领域应用广泛。六、数组与链表的对比数组和链表是两种最基础的线性数据结构各有优劣。特性数组链表内存分配连续内存静态或动态扩容非连续内存节点分散随机访问O(1)O(n)插入/删除头部/中间O(n)需移动元素O(1)给定节点位置插入/删除尾部均摊 O(1)动态数组O(1)有尾指针内存开销低仅元素本身高额外存储指针缓存友好非常友好连续内存不友好节点可能不连续选择数组还是链表取决于对随机访问、插入删除频率的要求。对于大量随机访问的场景数组是首选对于频繁在中间插入删除的场景链表可能更合适。七、数组的复杂度总结操作静态数组动态数组均摊随机访问O(1)O(1)末尾插入不支持O(1)中间插入O(n)O(n)头部插入O(n)O(n)删除末尾不支持O(1)删除中间O(n)O(n)搜索O(n)O(n)*动态数组的末尾插入均摊 O(1) 依赖于扩容时的复制操作平摊到各次插入中。八、数组的经典应用数组作为数据结构之母应用无处不在存储同类型数据集合例如学生成绩、温度记录、图像像素。实现其他数据结构栈、队列、哈希表开放寻址法、堆完全二叉树等底层常用数组。矩阵运算在科学计算、机器学习中矩阵用二维数组表示进行加减乘除、转置等操作。字符串处理字符串本质上就是字符数组。缓存与缓冲区如音频缓冲区、环形缓冲区循环数组用于流式数据处理。排序与查找算法几乎所有的排序快排、归并等和查找二分查找都基于数组。九、数组的局限与优化插入删除效率低因为需要移动大量元素。如果这类操作频繁考虑使用链表或平衡树。固定类型静态语言限制了灵活性但提高了安全性和性能。空间预分配动态数组扩容可能导致短暂的内存复制开销但可以通过合理的扩容策略如 1.5 倍来平衡。在现代编程中Python 的list、NumPy 的ndarray提供了更高效的数值计算都是数组的进化形态开发者可以根据需要选择。十、总结数组是一种简单而强大的数据结构它凭借连续内存和随机访问的特性成为编程中最基础的抽象之一。通过本文你应该掌握了数组的本质连续内存 同类型元素 索引访问。基本操作及其时间复杂度访问 O(1)、搜索 O(n)、插入删除 O(n)末尾 O(1)。静态数组与动态数组的区别与实现。多维数组的存储方式。数组与链表的对比及适用场景。数组在计算机科学中的广泛应用。数组虽小却承载着程序设计的核心思想。无论是初学者还是资深开发者深入理解数组都能在算法设计、性能优化中游刃有余。
数据结构——数组
发布时间:2026/6/10 15:22:11
在数据结构的世界里数组Array是最基础、最古老、也是应用最广泛的结构之一。它就像一个整齐划一的公寓楼——每个房间编号连续大小相同通过门牌号索引可以瞬间找到任何一间房间。正是这种连续内存和随机访问的特性使数组成为几乎所有高级数据结构的底层构建模块。本文将从数组的定义、内存模型、基本操作、动态数组的实现、多维数组、复杂度分析以及典型应用等方面带你全面理解数组。一、什么是数组数组是一种线性数据结构它用一组连续的内存空间存储相同类型的数据元素并通过索引下标来访问每个元素。在大多数编程语言中数组的索引从 0 开始。数组的核心特点可以概括为连续内存所有元素在内存中紧密排列没有空隙。固定类型数组中的元素通常具有相同的数据类型在静态类型语言中强制在动态语言如 Python 中虽可混合但底层仍是同质。随机访问通过索引可以在 O(1) 时间内直接访问任意元素。静态或动态有的语言提供静态数组长度固定有的提供动态数组可自动扩容。二、数组的内存模型理解数组的关键是理解它的内存布局。假设我们声明一个整型数组int arr[5]内存模型如下地址元素0x1000arr[0]0x1004arr[1]0x1008arr[2]0x100Carr[3]0x1010arr[4]由于每个int占 4 字节在大多数系统上相邻元素的内存地址相差 4 字节。当我们写arr[i]时编译器会计算实际地址base_address i * element_size从而直接定位到目标元素无需遍历。这种“公式计算”就是数组 O(1) 随机访问的本质。三、数组的基本操作数组支持的操作相对简单但不同操作的时间复杂度差异巨大。1. 访问Access给定索引 i直接获取元素值。时间复杂度O(1)。# Python 列表作为动态数组 arr [10, 20, 30, 40, 50] element arr[2] # 302. 搜索Search查找某个值是否存在通常需要遍历数组。时间复杂度O(n)无序情况下。def search(arr, target): for i, val in enumerate(arr): if val target: return i return -13. 插入Insert在末尾插入通常 O(1)动态数组需要考虑扩容。在中间/开头插入需要移动后续元素平均 O(n)。# Python 列表的 insert 操作 arr [1, 2, 3, 4, 5] arr.insert(2, 99) # 在索引 2 处插入 99后面的元素右移 print(arr) # [1, 2, 99, 3, 4, 5]4. 删除Delete删除末尾元素O(1)。删除中间/开头元素需要移动后续元素填补空缺平均 O(n)。# 删除索引 2 的元素 del arr[2] # 后面元素左移5. 更新Update通过索引赋值O(1)。arr[2] 100四、数组的实现静态数组与动态数组1. 静态数组Static Array长度在声明时确定之后不可改变。C 语言中的原生数组是典型代表。int arr[10]; // 固定容量为 10优点内存分配一次完成无扩容开销。缺点容量固定可能浪费空间或不够用。2. 动态数组Dynamic Array可以自动扩容的数组在 Python 中就是list在 Java 中是ArrayList在 C 中是vector。动态数组的实现原理底层使用静态数组存储元素。维护一个容量capacity和一个实际大小size。当 size 达到 capacity 时创建一块更大的新数组通常扩容为原容量的 1.5 倍或 2 倍复制旧元素然后释放旧数组。Python 列表的简化实现示例class DynamicArray: def __init__(self, capacity10): self._data [None] * capacity self._capacity capacity self._size 0 def append(self, item): if self._size self._capacity: self._resize(2 * self._capacity) # 扩容为 2 倍 self._data[self._size] item self._size 1 def insert(self, index, item): if self._size self._capacity: self._resize(2 * self._capacity) # 将 index 及之后的元素右移 for i in range(self._size, index, -1): self._data[i] self._data[i-1] self._data[index] item self._size 1 def pop(self): if self._size 0: raise IndexError(pop from empty array) item self._data[self._size - 1] self._size - 1 # 可选缩容避免浪费过多空间 if self._size self._capacity // 4: self._resize(self._capacity // 2) return item def _resize(self, new_capacity): new_data [None] * new_capacity for i in range(self._size): new_data[i] self._data[i] self._data new_data self._capacity new_capacity def __getitem__(self, index): if index 0 or index self._size: raise IndexError(index out of range) return self._data[index] def __setitem__(self, index, value): if index 0 or index self._size: raise IndexError(index out of range) self._data[index] value def __len__(self): return self._size扩容策略通常采用倍数扩容如 2 倍使得均摊时间复杂度为 O(1)。如果每次只增加固定大小会导致频繁扩容性能下降。五、多维数组数组可以有多维形式最常见的是二维数组矩阵。在内存中多维数组通常以行优先Row-major或列优先Column-major的方式存储。# Python 中使用嵌套列表表示二维数组 matrix [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] # 访问第二行第三列索引从0开始 element matrix[1][2] # 6二维数组的随机访问依然 O(1)matrix[i][j]实际地址 base (i * cols j) * elem_size行优先。多维数组在图像处理、科学计算、机器学习等领域应用广泛。六、数组与链表的对比数组和链表是两种最基础的线性数据结构各有优劣。特性数组链表内存分配连续内存静态或动态扩容非连续内存节点分散随机访问O(1)O(n)插入/删除头部/中间O(n)需移动元素O(1)给定节点位置插入/删除尾部均摊 O(1)动态数组O(1)有尾指针内存开销低仅元素本身高额外存储指针缓存友好非常友好连续内存不友好节点可能不连续选择数组还是链表取决于对随机访问、插入删除频率的要求。对于大量随机访问的场景数组是首选对于频繁在中间插入删除的场景链表可能更合适。七、数组的复杂度总结操作静态数组动态数组均摊随机访问O(1)O(1)末尾插入不支持O(1)中间插入O(n)O(n)头部插入O(n)O(n)删除末尾不支持O(1)删除中间O(n)O(n)搜索O(n)O(n)*动态数组的末尾插入均摊 O(1) 依赖于扩容时的复制操作平摊到各次插入中。八、数组的经典应用数组作为数据结构之母应用无处不在存储同类型数据集合例如学生成绩、温度记录、图像像素。实现其他数据结构栈、队列、哈希表开放寻址法、堆完全二叉树等底层常用数组。矩阵运算在科学计算、机器学习中矩阵用二维数组表示进行加减乘除、转置等操作。字符串处理字符串本质上就是字符数组。缓存与缓冲区如音频缓冲区、环形缓冲区循环数组用于流式数据处理。排序与查找算法几乎所有的排序快排、归并等和查找二分查找都基于数组。九、数组的局限与优化插入删除效率低因为需要移动大量元素。如果这类操作频繁考虑使用链表或平衡树。固定类型静态语言限制了灵活性但提高了安全性和性能。空间预分配动态数组扩容可能导致短暂的内存复制开销但可以通过合理的扩容策略如 1.5 倍来平衡。在现代编程中Python 的list、NumPy 的ndarray提供了更高效的数值计算都是数组的进化形态开发者可以根据需要选择。十、总结数组是一种简单而强大的数据结构它凭借连续内存和随机访问的特性成为编程中最基础的抽象之一。通过本文你应该掌握了数组的本质连续内存 同类型元素 索引访问。基本操作及其时间复杂度访问 O(1)、搜索 O(n)、插入删除 O(n)末尾 O(1)。静态数组与动态数组的区别与实现。多维数组的存储方式。数组与链表的对比及适用场景。数组在计算机科学中的广泛应用。数组虽小却承载着程序设计的核心思想。无论是初学者还是资深开发者深入理解数组都能在算法设计、性能优化中游刃有余。