链表和数组
一、Python 中的“数组”与“链表”的实现区别
类型 | Python 中的对应实现 | 底层结构 | 备注 |
---|---|---|---|
数组 | list 或 array.array | 连续内存块(C 数组) | list 是动态数组,可自动扩容 |
链表 | 需自定义 Node 类 | 非连续内存块(通过指针连接) | Python 默认没有内建链表,只能自己实现单链表 / 双链表 |
class Node:def __init__(self, val):self.val = valself.next = None
二、各操作的时间复杂度对比
操作 | 数组(Python list) | 链表(单/双向) | 说明 |
---|---|---|---|
按索引访问 a[i] | 🟩 O(1) | 🔴 O(n) | 数组支持随机访问,链表需遍历 |
按值查找 x in a | 🔴 O(n) | 🔴 O(n) | 都需遍历 |
在末尾插入 append() | 🟩 O(1)* 均摊 | 🟩 O(1) | 数组可能触发扩容;链表只需改尾指针 |
在头部插入 | 🔴 O(n) | 🟩 O(1) | 数组需移动所有元素 |
在中间插入 | 🟠 O(n) | 🟠 O(n) | 都需遍历到插入位置;数组还要搬移元素 |
删除末尾元素 pop() | 🟩 O(1) | 🟩 O(1) | 简单修改尾指针 |
删除头部元素 | 🔴 O(n) | 🟩 O(1) | 数组需搬移剩余元素 |
删除中间元素 | 🟠 O(n) | 🟠 O(n) | 需遍历定位;数组还要移动剩余元素 |
遍历全部元素 | 🟩 O(n) | 🟩 O(n) | 基本相同 |
随机插入/删除多个元素 | 🟠 O(n²) | 🟠 O(n²) | 都较慢,但链表避免了大规模搬移 |
扩容成本 | ⚠️ O(n)(偶发) | 无 | 动态数组需偶尔重新分配内存 |
三、空间复杂度对比
项目 | 数组(list) | 链表 |
---|---|---|
存储数据 | O(n) | O(n) |
额外开销 | O(1) | O(n)(每个节点需存放指针) |
空间局部性 | 优(连续) | 差(分散) |
数组 内存连续、空间利用率高,但扩容时需要一次性分配更大的内存。
链表 每个节点需额外存放指针,导致 内存占用更高 且 缓存性能较差。
四、Python 实际表现说明
当数组空间满了:
Python 会申请一块 更大的新内存(通常是原容量的约 1.125~2 倍)。
然后将旧数组中的所有元素 逐个复制到新内存中。
最后释放旧的那块内存。
Python 的
list
是一个 动态数组,在底层是通过 预分配空间 + 按需扩容 实现的。因此
append()
平均为 O(1),偶尔触发扩容时为 O(n)Python 没有原生链表,但
collections.deque
(双端队列)在某些场景下可视为“高效链表结构”:头尾插入、删除:O(1)
随机访问:O(n)