一、数组:连续内存的高效访问
1.1 数组的核心特性与内存模型
数组是最基础的线性数据结构,其核心在于连续内存分配。这种存储方式带来了独特的性能特征:
python
# 数组的内存访问演示
import ctypes
class DynamicArray:
def __init__(self):
self._n = 0 # 当前元素数量
self._capacity = 1 # 初始容量
self._A = self._make_array(self._capacity) # 底层数组
def __len__(self):
return self._n
def __getitem__(self, k):
"""随机访问:时间复杂度O(1)"""
if not 0 <= k < self._n:
raise IndexError('索引越界')
return self._A[k] # 直接计算内存地址:base_address + k * element_size
def append(self, obj):
"""追加元素:均摊时间复杂度O(1)"""
if self._n == self._capacity:
self._resize(2 * self._capacity) # 动态扩容
self._A[self._n] = obj
self._n += 1
def _resize(self, c):
"""扩容操作:时间复杂度O(n)"""
B = self._make_array(c)
for k in range(self._n):
B[k] = self._A[k]
self._A = B
self._capacity = c
def _make_array(self, c):
return (c * ctypes.py_object)()
数组操作的复杂度分析:
✅ 随机访问 :O(1) - 通过索引直接计算内存地址
❌ 插入/删除 :O(n) - 需要移动后续元素
⚠️ 动态扩容 :均摊分析后追加操作仍为O(1)
1.2 多维数组的存储方式
理解多维数组的内存布局对性能优化至关重要:
python
# 二维数组的行优先 vs 列优先存储
def matrix_access_demo():
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
print("行优先遍历(缓存友好):")
for i in range(3):
for j in range(3): # 内层循环连续访问内存
print(matrix[i][j], end=' ')
print()
print("列优先遍历(缓存不友好):")
for j in range(3):
for i in range(3): # 内层循环跳跃访问
print(matrix[i][j], end=' ')
print()
# 实际应用:图像处理中的矩阵操作
def image_processing_example():
# 假设一个3x3的灰度图像
image = [[100, 120, 110],
[130, 140, 125],
[115, 135, 145]]
# 行优先处理更高效
for row in image:
for pixel in row:
process_pixel(pixel) # 连续内存访问
1.3 动态数组的工程实践
Python列表的实现原理:
python
# 模拟Python列表的动态扩容策略
class PyListSimulator:
def __init__(self):
self.size = 0
self.capacity = 4 # 初始容量
self.data = [None] * self.capacity
self.growth_factor = 2 # 增长因子
def append(self, item):
if self.size == self.capacity:
self._grow()
self.data[self.size] = item
self.size += 1
def _grow(self):
new_capacity = int(self.capacity * self.growth_factor)
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
print(f"扩容:{self.capacity//self.growth_factor} -> {self.capacity}")
二、链表:灵活的非连续存储
2.1 链表的基本结构与类型
链表通过指针连接节点,提供了动态内存管理的灵活性:
python
class ListNode:
"""链表节点定义"""
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class LinkedList:
"""单链表实现"""
def __init__(self):
self.head = None
self.size = 0
def get(self, index: int) -> int:
"""按索引访问:时间复杂度O(n)"""
if index < 0 or index >= self.size:
return -1
curr = self.head
for _ in range(index):
curr = curr.next
return curr.val
def add_at_head(self, val: int) -> None:
"""头插法:时间复杂度O(1)"""
new_node = ListNode(val)
new_node.next = self.head
self.head = new_node
self.size += 1
def add_at_tail(self, val: int) -> None:
"""尾插法:时间复杂度O(n)"""
new_node = ListNode(val)
if not self.head:
self.head = new_node
else:
curr = self.head
while curr.next:
curr = curr.next
curr.next = new_node
self.size += 1
2.2 双链表与循环链表
双向链表提供了前后双向遍历的能力:
python
class DoublyListNode:
def __init__(self, val=0):
self.val = val
self.prev = None
self.next = None
class DoublyLinkedList:
def __init__(self):
self.head = None
self.tail = None
self.size = 0
def add_first(self, val):
"""在链表头部添加节点"""
new_node = DoublyListNode(val)
if not self.head:
self.head = self.tail = new_node
else:
new_node.next = self.head
self.head.prev = new_node
self.head = new_node
self.size += 1
def remove_last(self):
"""删除尾节点"""
if not self.tail:
return None
removed = self.tail
if self.head == self.tail:
self.head = self.tail = None
else:
self.tail = self.tail.prev
self.tail.next = None
self.size -= 1
return removed.val
2.3 链表经典算法题解
1. 链表反转(迭代法):
python
def reverse_list(head):
prev = None
curr = head
while curr:
next_temp = curr.next # 保存下一个节点
curr.next = prev # 反转指针
prev = curr # 移动prev
curr = next_temp # 移动curr
return prev
2. 快慢指针应用:
python
def has_cycle(head):
"""检测链表是否有环"""
if not head or not head.next:
return False
slow = head
fast = head.next
while slow != fast:
if not fast or not fast.next:
return False
slow = slow.next
fast = fast.next.next
return True
def middle_node(head):
"""找到链表的中间节点"""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
3. LRU缓存实现:
python
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.cache = {}
self.head = DoublyListNode(0) # 伪头节点
self.tail = DoublyListNode(0) # 伪尾节点
self.head.next = self.tail
self.tail.prev = self.head
def _add_node(self, node):
"""在头部添加节点"""
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def _remove_node(self, node):
"""删除节点"""
prev = node.prev
next_node = node.next
prev.next = next_node
next_node.prev = prev
def _move_to_head(self, node):
"""移动节点到头部"""
self._remove_node(node)
self._add_node(node)
def get(self, key: int) -> int:
node = self.cache.get(key, None)
if not node:
return -1
self._move_to_head(node)
return node.value
def put(self, key: int, value: int) -> None:
node = self.cache.get(key)
if not node:
new_node = DoublyListNode(value)
new_node.key = key
self.cache[key] = new_node
self._add_node(new_node)
if len(self.cache) > self.capacity:
tail = self.tail.prev
self._remove_node(tail)
del self.cache[tail.key]
else:
node.value = value
self._move_to_head(node)
三、栈:LIFO的线性结构
3.1 栈的基本操作与实现
栈遵循后进先出原则,支持三种基本操作:
cs
class ArrayStack:
"""基于数组的栈实现"""
def __init__(self):
self._items = []
def push(self, item):
"""入栈:O(1)"""
self._items.append(item)
def pop(self):
"""出栈:O(1)"""
if self.is_empty():
raise Exception("栈为空")
return self._items.pop()
def peek(self):
"""查看栈顶:O(1)"""
if self.is_empty():
raise Exception("栈为空")
return self._items[-1]
def is_empty(self):
return len(self._items) == 0
def size(self):
return len(self._items)
class LinkedStack:
"""基于链表的栈实现"""
class _Node:
def __init__(self, item):
self.item = item
self.next = None
def __init__(self):
self._top = None
self._size = 0
def push(self, item):
new_node = self._Node(item)
new_node.next = self._top
self._top = new_node
self._size += 1
def pop(self):
if self.is_empty():
raise Exception("栈为空")
item = self._top.item
self._top = self._top.next
self._size -= 1
return item
def peek(self):
if self.is_empty():
raise Exception("栈为空")
return self._top.item
def is_empty(self):
return self._top is None
def size(self):
return self._size
3.2 栈的经典应用场景
1. 括号匹配校验:
python
def is_valid_parentheses(s: str) -> bool:
stack = []
mapping = {')': '(', '}': '{', ']': '['}
for char in s:
if char in mapping.values(): # 左括号入栈
stack.append(char)
elif char in mapping: # 右括号匹配
if not stack or stack[-1] != mapping[char]:
return False
stack.pop()
else:
return False
return not stack # 栈应为空
# 测试用例
print(is_valid_parentheses("()[]{}")) # True
print(is_valid_parentheses("([)]")) # False
2. 表达式求值:
python
def evaluate_expression(expression: str) -> int:
def apply_operator(operators, values):
operator = operators.pop()
right = values.pop()
left = values.pop()
if operator == '+': values.append(left + right)
elif operator == '-': values.append(left - right)
elif operator == '*': values.append(left * right)
elif operator == '/': values.append(int(left / right))
precedence = {'+': 1, '-': 1, '*': 2, '/': 2}
values = []
operators = []
i = 0
while i < len(expression):
if expression[i] == ' ':
i += 1
continue
if expression[i].isdigit(): # 处理数字
j = i
while j < len(expression) and expression[j].isdigit():
j += 1
values.append(int(expression[i:j]))
i = j
elif expression[i] == '(': # 左括号
operators.append(expression[i])
i += 1
elif expression[i] == ')': # 右括号
while operators and operators[-1] != '(':
apply_operator(operators, values)
operators.pop() # 弹出左括号
i += 1
else: # 运算符
while (operators and operators[-1] != '(' and
precedence[operators[-1]] >= precedence[expression[i]]):
apply_operator(operators, values)
operators.append(expression[i])
i += 1
while operators:
apply_operator(operators, values)
return values[0]
四、队列:FIFO的线性结构
4.1 队列的基本实现
基于数组的简单队列:
python
class ArrayQueue:
def __init__(self, capacity=10):
self._items = [None] * capacity
self._front = 0
self._rear = 0
self._size = 0
def enqueue(self, item):
"""入队:O(1)"""
if self._size == len(self._items):
self._resize(2 * len(self._items))
self._items[self._rear] = item
self._rear = (self._rear + 1) % len(self._items)
self._size += 1
def dequeue(self):
"""出队:O(1)"""
if self.is_empty():
raise Exception("队列为空")
item = self._items[self._front]
self._items[self._front] = None
self._front = (self._front + 1) % len(self._items)
self._size -= 1
return item
def _resize(self, capacity):
old_items = self._items
self._items = [None] * capacity
walk = self._front
for i in range(self._size):
self._items[i] = old_items[walk]
walk = (walk + 1) % len(old_items)
self._front = 0
self._rear = self._size
4.2 循环队列解决假溢出
循环队列实现:
python
class CircularQueue:
def __init__(self, k: int):
self.queue = [None] * k
self.head = 0
self.tail = 0
self.count = 0
self.capacity = k
def enqueue(self, value: int) -> bool:
if self.is_full():
return False
self.queue[self.tail] = value
self.tail = (self.tail + 1) % self.capacity
self.count += 1
return True
def dequeue(self) -> bool:
if self.is_empty():
return False
self.head = (self.head + 1) % self.capacity
self.count -= 1
return True
def front(self) -> int:
if self.is_empty():
return -1
return self.queue[self.head]
def rear(self) -> int:
if self.is_empty():
return -1
return self.queue[(self.tail - 1) % self.capacity]
def is_empty(self) -> bool:
return self.count == 0
def is_full(self) -> bool:
return self.count == self.capacity
4.3 双端队列与应用
双端队列实现:
python
class Deque:
def __init__(self):
self._items = []
def add_front(self, item):
self._items.insert(0, item)
def add_rear(self, item):
self._items.append(item)
def remove_front(self):
if self.is_empty():
raise Exception("双端队列为空")
return self._items.pop(0)
def remove_rear(self):
if self.is_empty():
raise Exception("双端队列为空")
return self._items.pop()
def is_empty(self):
return len(self._items) == 0
def size(self):
return len(self._items)
滑动窗口最大值问题:
python
def max_sliding_window(nums, k):
from collections import deque
if not nums:
return []
result = []
dq = deque() # 存储索引
for i in range(len(nums)):
# 移除超出窗口范围的元素
if dq and dq[0] < i - k + 1:
dq.popleft()
# 移除比当前元素小的元素
while dq and nums[dq[-1]] < nums[i]:
dq.pop()
dq.append(i)
# 当窗口形成时记录最大值
if i >= k - 1:
result.append(nums[dq[0]])
return result
五、学习建议与实践指南
5.1 手写实现练习
建议完成的实现练习:
python
# 练习1:实现带迭代器的链表
class LinkedListWithIterator:
def __init__(self):
self.head = None
def __iter__(self):
current = self.head
while current:
yield current.val
current = current.next
# 练习2:实现最小栈
class MinStack:
def __init__(self):
self.stack = []
self.min_stack = [] # 辅助栈记录最小值
def push(self, x: int) -> None:
self.stack.append(x)
if not self.min_stack or x <= self.min_stack[-1]:
self.min_stack.append(x)
def pop(self) -> None:
if self.stack.pop() == self.min_stack[-1]:
self.min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def get_min(self) -> int:
return self.min_stack[-1]
总结
线性数据结构是算法学习的基石,掌握它们的特性和应用场景至关重要:
数组:连续内存,随机访问高效,适合读多写少的场景
链表:动态内存,插入删除高效,适合频繁修改的场景
栈:LIFO特性,适合需要"撤销"操作的场景
队列 :FIFO特性,适合任务调度和缓冲场景
理解每种结构的时间复杂度特征 和适用场景,能够帮助我们在实际编程中做出正确的选择。下一阶段我们将进入非线性数据结构的学习,包括树、堆、图等更复杂的数据结构。