【数据结构与算法学习笔记】数组与链表
前言
本文为个人学习的算法学习笔记,学习笔记,学习笔记,不是经验分享与教学,不是经验分享与教学,不是经验分享与教学,若有错误各位大佬轻喷(T^T)。主要使用编程语言为Python3,各类资料题目源于网络,主要自学途径为博学谷,侵权即删。
1. 数组(Array)
1.1 定义与 Python 特性
数组是 “定义了如何组织、存储和管理数据” 的线性结构,核心特征是连续内存空间存储相同类型数据、长度固定(创建后不可改)、通过下标 / 索引操作数据<RichMediaReference>。Python 中无原生 “固定长度数组”,但可通过list
(列表)模拟数组的核心行为(注意:list
支持动态扩容,需手动限制以贴合数组 “长度固定” 特性),且list
的元素访问逻辑与数组一致 —— 通过下标快速定位。
1.2 核心操作(Python 示例)
(1)查询 / 访问:O (1)(已知下标)、O (n)(未知下标)
-
随机访问(已知下标):直接通过
列表[下标]
获取元素,无需遍历,时间复杂度 O (1),对应数组 “通过下标操作数据” 的特性<RichMediaReference>。示例:# 模拟数组(限定元素类型为int,手动固定长度) arr = [4, 5, 1, 3] # 基地址可理解为列表在内存中的起始位置(Python无需手动管理地址) # 访问下标2的元素(O(1)) print(arr[2]) # 输出:1,对应公式a[i]_addr = 基地址 + i×数据类型大小<RichMediaReference>
-
顺序查找(未知下标):需从第一个元素遍历至目标元素,最坏情况遍历全部元素,时间复杂度 O (n)<RichMediaReference>。示例:
def linear_search(arr, target):for i in range(len(arr)):if arr[i] == target:return i # 返回目标下标return -1 # 未找到arr = [4, 5, 1, 3] print(linear_search(arr, 5)) # 输出:1(遍历2次找到) print(linear_search(arr, 6)) # 输出:-1(遍历4次,O(n))
(2)插入:O (1)(替换)、O (n)(中间 / 开头插入)
-
替换(已知下标):直接覆盖目标下标元素,无需移动其他数据,时间复杂度 O (1)<RichMediaReference>。示例:
arr = [4, 5, 1, 3] arr[2] = 8 # 替换下标2的元素为8(O(1)) print(arr) # 输出:[4, 5, 8, 3]
-
中间 / 开头插入(需移动元素):Python
list.insert(下标, 元素)
底层会移动插入位置及后续元素,腾出空间,时间复杂度 O (n)<RichMediaReference>。示例:arr = [4, 5, 1, 3] # 在下标2插入8(需移动1、3至后一位,O(n)) arr.insert(2, 8) print(arr) # 输出:[4, 5, 8, 1, 3]
(3)删除:O (n)(需保持连续)
Pythonlist.pop(下标)
会删除目标元素,并将后续元素向前移动以保持连续,最坏情况移动 n 个元素,时间复杂度 O (n)<RichMediaReference>。示例:
arr = [4, 5, 8, 1, 3]
# 删除下标1的元素(5),需移动8、1、3至前一位(O(n))
arr.pop(1)
print(arr) # 输出:[4, 8, 1, 3]
2. 链表(Linked List)
2.1 定义与节点结构
链表是 “占用非连续内存空间、节点之间通过指针连接、天然支持扩容” 的线性结构,每个节点包含数据域(data) 和指针域(next,指向后续节点),链表两端分别为头节点(head)和尾节点(tail,tail.next = None)。Python 中通过类
定义节点,通过next
属性模拟指针连接。
2.2 核心操作(Python 示例)
(1)节点定义
class Node:def __init__(self, data):self.data = data # 数据域:存储元素值self.next = None # 指针域:初始指向None(无后续节点)
(2)遍历:O (n)
需从head
开始,通过next
指针依次访问每个节点,直到next
为 None,时间复杂度 O (n)。示例:
# 构建链表:head → 2 → 3 → 1 → 7 → tail(tail.next = None)
head = Node(2)
node2 = Node(3)
node3 = Node(1)
node4 = Node(7)
head.next = node2
node2.next = node3
node3.next = node4
tail = node4# 遍历链表
def traverse_linked_list(head):current = headwhile current is not None: # 直到current为None(遍历结束)print(current.data, end=" → ")current = current.nextprint("None")traverse_linked_list(head) # 输出:2 → 3 → 1 → 7 → None
(3)插入:O (1)(已知前驱节点)
插入的核心是 “修改next
指针”,无需移动元素,若已知前驱节点,时间复杂度 O (1)。常见插入场景:
-
尾部插入:直接修改尾节点
next
指向新节点,更新尾节点。示例:# 在尾节点7后插入新节点6 new_node = Node(6) tail.next = new_node # 原尾节点(7)的next指向6 tail = new_node # 更新尾节点为6 tail.next = None # 确保尾节点next为None traverse_linked_list(head) # 输出:2 → 3 → 1 → 7 → 6 → None
-
中间插入(已知前驱节点):假设在节点
node2
(值 3)后插入新节点5
。示例:new_node = Node(5) # 步骤1:新节点next指向前驱节点的原后续节点(node3) new_node.next = node2.next # 步骤2:前驱节点next指向新节点 node2.next = new_node traverse_linked_list(head) # 输出:2 → 3 → 5 → 1 → 7 → 6 → None
(4)删除:O (1)(已知前驱节点)
删除的核心是 “跳过待删除节点,修改前驱节点next
”,无需移动元素,时间复杂度 O (1)。示例:删除中间节点node3
(值 1,前驱节点为new_node
(值 5)):
# 步骤1:找到前驱节点(pre_node = new_node,待删除节点 = pre_node.next)
pre_node = new_node
del_node = pre_node.next
# 步骤2:前驱节点next指向待删除节点的后续节点(node4)
pre_node.next = del_node.next
# 步骤3:待删除节点next置为None(Python会自动回收内存)
del_node.next = None
traverse_linked_list(head) # 输出:2 → 3 → 5 → 7 → 6 → None
(5)查询:O (n)
需从head
遍历链表,逐一比对data
,最坏情况遍历全部节点,时间复杂度 O (n)。示例:查找值为 7 的节点:
def search_linked_list(head, target):current = headindex = 0while current is not None:if current.data == target:return f"目标值{target}在链表中,位置(从0开始):{index}"current = current.nextindex += 1return f"目标值{target}不在链表中"print(search_linked_list(head, 7)) # 输出:目标值7在链表中,位置(从0开始):3
print(search_linked_list(head, 9)) # 输出:目标值9不在链表中
3. 数组与链表核心对比(Python 视角)
对比维度 | 数组(Python list 模拟) | 链表(Python 类实现) | 文档依据 |
---|---|---|---|
内存存储 | 连续内存(list 底层实现) | 非连续内存(节点分散存储) | |
长度特性 | 支持动态扩容(需手动限制) | 天然动态扩容(新增节点即可) | |
访问效率 | 下标访问 O (1);查找 O (n) | 遍历 / 查找均 O (n) | |
插入删除效率 | 中间操作 O (n)(移动元素) | 已知前驱节点 O (1)(改指针) | |
Python 适用场景 | 频繁访问(如成绩查询、数据统计) | 频繁插入删除(如消息队列、任务调度) |
关键结论
- 若需频繁通过下标访问数据,优先用数组(Python list);
- 若需频繁在中间插入 / 删除数据,优先用链表(Python 类实现),二者选择需贴合文档中 “数据结构与算法相辅相成” 的核心逻辑。