链表(Linked List)
链表(Linked List)简介
链表是另一种常见的线性数据结构,与数组不同,链表的元素在内存中不必连续存储,而是通过指针(或引用)将各个元素连接起来。
基本特性
- 动态大小:链表的大小可以动态增长或缩小
- 非连续内存:元素可以分散存储在内存中的任何位置
- 顺序访问:必须从头节点开始逐个访问,不能随机访问
- 节点结构:每个节点包含数据域和指针域(指向下一个节点)
链表的主要类型
-
单向链表(Singly Linked List)
- 每个节点包含数据和指向下一个节点的指针
- 只能单向遍历
-
双向链表(Doubly Linked List)
- 每个节点包含数据、指向前驱节点的指针和指向后继节点的指针
- 可以双向遍历
-
循环链表(Circular Linked List)
- 单向链表或双向链表的变种,尾节点指向头节点形成环
- 单向循环链表:尾节点指向头节点
- 双向循环链表:尾节点指向头节点,头节点也指向尾节点
链表的操作复杂度
| 操作 | 时间复杂度 |
|---|---|
| 访问元素 | O(n) |
| 插入/删除(已知位置) | O(1) |
| 插入/删除(需要查找位置) | O(n) |
| 查找元素 | O(n) |
| 在头部插入/删除 | O(1) |
| 在尾部插入(无尾指针) | O(n) |
| 在尾部插入(有尾指针) | O(1) |
| 在尾部删除(无尾指针) | O(n) |
| 在尾部删除(有尾指针) | O(1)(双链表)或O(n)(单链表) |
链表的优缺点
优点:
- 动态大小,无需预先知道数据规模
- 高效的插入和删除操作(特别是已知位置时,如果不知道位置最差的时间复杂度是 O(n))
- 内存利用率高(不需要连续内存空间)
缺点:
- 访问元素效率低(必须从头开始遍历)
- 需要额外空间存储指针
- 缓存不友好(数据分散在内存各处)
- 实现比数组复杂
链表的典型应用
- 实现栈、队列等抽象数据类型
- 实现图的邻接表表示
- 内存管理系统
- 实现哈希表的链地址法
- 需要频繁插入删除的场景
- 浏览器历史记录(前进后退功能)
- 音乐播放列表
单向链表代码实现
class Node:
"""链表节点定义"""
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
"""单链表实现"""
def __init__(self):
self.head = None
def append(self, data):
"""在链表末尾添加节点"""
new_node = Node(data)
if not self.head:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
def delete(self, key):
"""删除包含指定值的节点"""
curr = self.head
# 如果头节点就是要删除的节点
if curr and curr.data == key:
self.head = curr.next
curr = None
return
# 查找要删除的节点
prev = None
while curr and curr.data != key:
prev = curr
curr = curr.next
# 如果找到则删除
if curr:
prev.next = curr.next
curr = None
def update(self, old_data, new_data):
"""更新节点值"""
curr = self.head
while curr:
if curr.data == old_data:
curr.data = new_data
return
curr = curr.next
print(f"未找到值为 {old_data} 的节点")
# 使用示例
if __name__ == "__main__":
ll = LinkedList()
ll.append(1)
ll.append(2)
ll.append(3)
print("原始链表:")
current = ll.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
ll.update(2, 20)
ll.delete(1)
print("\n修改后的链表:")
current = ll.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
链表与数组的比较
| 特性 | 数组 | 链表 |
|---|---|---|
| 内存分配 | 连续 | 分散 |
| 大小 | 固定(静态数组) | 动态 |
| 访问方式 | 随机访问 | 顺序访问 |
| 访问时间复杂度 | O(1) | O(n) |
| 插入/删除时间复杂度 | O(n) | O(1)(已知位置) |
| 内存开销 | 仅数据 | 数据+指针 |
| 缓存友好性 | 好 | 差 |
链表和数组各有优劣,选择使用哪种数据结构取决于具体的应用场景和操作需求。

浙公网安备 33010602011771号