线性数据结构深度解析:数组、链表、栈与队列的实现与应用
一、数组:连续内存的高效访问
1.1 数组的核心特性与内存模型
数组是最基础的线性数据结构,其核心在于连续内存分配。这种存储方式带来了独特的性能特征:
# 数组的内存访问演示
import ctypesclass DynamicArray:def __init__(self):self._n = 0 # 当前元素数量self._capacity = 1 # 初始容量self._A = self._make_array(self._capacity) # 底层数组def __len__(self):return self._ndef __getitem__(self, k):"""随机访问:时间复杂度O(1)"""if not 0 <= k < self._n:raise IndexError('索引越界')return self._A[k] # 直接计算内存地址:base_address + k * element_sizedef append(self, obj):"""追加元素:均摊时间复杂度O(1)"""if self._n == self._capacity:self._resize(2 * self._capacity) # 动态扩容self._A[self._n] = objself._n += 1def _resize(self, c):"""扩容操作:时间复杂度O(n)"""B = self._make_array(c)for k in range(self._n):B[k] = self._A[k]self._A = Bself._capacity = cdef _make_array(self, c):return (c * ctypes.py_object)()数组操作的复杂度分析:
✅ 随机访问:O(1) - 通过索引直接计算内存地址
❌ 插入/删除:O(n) - 需要移动后续元素
⚠️ 动态扩容:均摊分析后追加操作仍为O(1)
1.2 多维数组的存储方式
理解多维数组的内存布局对性能优化至关重要:
# 二维数组的行优先 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列表的动态扩容策略
class PyListSimulator:def __init__(self):self.size = 0self.capacity = 4 # 初始容量self.data = [None] * self.capacityself.growth_factor = 2 # 增长因子def append(self, item):if self.size == self.capacity:self._grow()self.data[self.size] = itemself.size += 1def _grow(self):new_capacity = int(self.capacity * self.growth_factor)new_data = [None] * new_capacityfor i in range(self.size):new_data[i] = self.data[i]self.data = new_dataself.capacity = new_capacityprint(f"扩容:{self.capacity//self.growth_factor} -> {self.capacity}")二、链表:灵活的非连续存储
2.1 链表的基本结构与类型
链表通过指针连接节点,提供了动态内存管理的灵活性:
class ListNode:"""链表节点定义"""def __init__(self, val=0, next=None):self.val = valself.next = nextclass LinkedList:"""单链表实现"""def __init__(self):self.head = Noneself.size = 0def get(self, index: int) -> int:"""按索引访问:时间复杂度O(n)"""if index < 0 or index >= self.size:return -1curr = self.headfor _ in range(index):curr = curr.nextreturn curr.valdef add_at_head(self, val: int) -> None:"""头插法:时间复杂度O(1)"""new_node = ListNode(val)new_node.next = self.headself.head = new_nodeself.size += 1def add_at_tail(self, val: int) -> None:"""尾插法:时间复杂度O(n)"""new_node = ListNode(val)if not self.head:self.head = new_nodeelse:curr = self.headwhile curr.next:curr = curr.nextcurr.next = new_nodeself.size += 12.2 双链表与循环链表
双向链表提供了前后双向遍历的能力:
class DoublyListNode:def __init__(self, val=0):self.val = valself.prev = Noneself.next = Noneclass DoublyLinkedList:def __init__(self):self.head = Noneself.tail = Noneself.size = 0def add_first(self, val):"""在链表头部添加节点"""new_node = DoublyListNode(val)if not self.head:self.head = self.tail = new_nodeelse:new_node.next = self.headself.head.prev = new_nodeself.head = new_nodeself.size += 1def remove_last(self):"""删除尾节点"""if not self.tail:return Noneremoved = self.tailif self.head == self.tail:self.head = self.tail = Noneelse:self.tail = self.tail.prevself.tail.next = Noneself.size -= 1return removed.val2.3 链表经典算法题解
1. 链表反转(迭代法):
def reverse_list(head):prev = Nonecurr = headwhile curr:next_temp = curr.next # 保存下一个节点curr.next = prev # 反转指针prev = curr # 移动prevcurr = next_temp # 移动currreturn prev2. 快慢指针应用:
def has_cycle(head):"""检测链表是否有环"""if not head or not head.next:return Falseslow = headfast = head.nextwhile slow != fast:if not fast or not fast.next:return Falseslow = slow.nextfast = fast.next.nextreturn Truedef middle_node(head):"""找到链表的中间节点"""slow = fast = headwhile fast and fast.next:slow = slow.nextfast = fast.next.nextreturn slow3. LRU缓存实现:
class LRUCache:def __init__(self, capacity: int):self.capacity = capacityself.cache = {}self.head = DoublyListNode(0) # 伪头节点self.tail = DoublyListNode(0) # 伪尾节点self.head.next = self.tailself.tail.prev = self.headdef _add_node(self, node):"""在头部添加节点"""node.prev = self.headnode.next = self.head.nextself.head.next.prev = nodeself.head.next = nodedef _remove_node(self, node):"""删除节点"""prev = node.prevnext_node = node.nextprev.next = next_nodenext_node.prev = prevdef _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 -1self._move_to_head(node)return node.valuedef put(self, key: int, value: int) -> None:node = self.cache.get(key)if not node:new_node = DoublyListNode(value)new_node.key = keyself.cache[key] = new_nodeself._add_node(new_node)if len(self.cache) > self.capacity:tail = self.tail.prevself._remove_node(tail)del self.cache[tail.key]else:node.value = valueself._move_to_head(node)三、栈:LIFO的线性结构
3.1 栈的基本操作与实现
栈遵循后进先出原则,支持三种基本操作:
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) == 0def size(self):return len(self._items)class LinkedStack:"""基于链表的栈实现"""class _Node:def __init__(self, item):self.item = itemself.next = Nonedef __init__(self):self._top = Noneself._size = 0def push(self, item):new_node = self._Node(item)new_node.next = self._topself._top = new_nodeself._size += 1def pop(self):if self.is_empty():raise Exception("栈为空")item = self._top.itemself._top = self._top.nextself._size -= 1return itemdef peek(self):if self.is_empty():raise Exception("栈为空")return self._top.itemdef is_empty(self):return self._top is Nonedef size(self):return self._size3.2 栈的经典应用场景
1. 括号匹配校验:
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 Falsestack.pop()else:return Falsereturn not stack # 栈应为空# 测试用例
print(is_valid_parentheses("()[]{}")) # True
print(is_valid_parentheses("([)]")) # False2. 表达式求值:
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 = 0while i < len(expression):if expression[i] == ' ':i += 1continueif expression[i].isdigit(): # 处理数字j = iwhile j < len(expression) and expression[j].isdigit():j += 1values.append(int(expression[i:j]))i = jelif expression[i] == '(': # 左括号operators.append(expression[i])i += 1elif expression[i] == ')': # 右括号while operators and operators[-1] != '(':apply_operator(operators, values)operators.pop() # 弹出左括号i += 1else: # 运算符while (operators and operators[-1] != '(' and precedence[operators[-1]] >= precedence[expression[i]]):apply_operator(operators, values)operators.append(expression[i])i += 1while operators:apply_operator(operators, values)return values[0]四、队列:FIFO的线性结构
4.1 队列的基本实现
基于数组的简单队列:
class ArrayQueue:def __init__(self, capacity=10):self._items = [None] * capacityself._front = 0self._rear = 0self._size = 0def enqueue(self, item):"""入队:O(1)"""if self._size == len(self._items):self._resize(2 * len(self._items))self._items[self._rear] = itemself._rear = (self._rear + 1) % len(self._items)self._size += 1def dequeue(self):"""出队:O(1)"""if self.is_empty():raise Exception("队列为空")item = self._items[self._front]self._items[self._front] = Noneself._front = (self._front + 1) % len(self._items)self._size -= 1return itemdef _resize(self, capacity):old_items = self._itemsself._items = [None] * capacitywalk = self._frontfor i in range(self._size):self._items[i] = old_items[walk]walk = (walk + 1) % len(old_items)self._front = 0self._rear = self._size4.2 循环队列解决假溢出
循环队列实现:
class CircularQueue:def __init__(self, k: int):self.queue = [None] * kself.head = 0self.tail = 0self.count = 0self.capacity = kdef enqueue(self, value: int) -> bool:if self.is_full():return Falseself.queue[self.tail] = valueself.tail = (self.tail + 1) % self.capacityself.count += 1return Truedef dequeue(self) -> bool:if self.is_empty():return Falseself.head = (self.head + 1) % self.capacityself.count -= 1return Truedef front(self) -> int:if self.is_empty():return -1return self.queue[self.head]def rear(self) -> int:if self.is_empty():return -1return self.queue[(self.tail - 1) % self.capacity]def is_empty(self) -> bool:return self.count == 0def is_full(self) -> bool:return self.count == self.capacity4.3 双端队列与应用
双端队列实现:
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) == 0def size(self):return len(self._items)滑动窗口最大值问题:
def max_sliding_window(nums, k):from collections import dequeif 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 手写实现练习
建议完成的实现练习:
# 练习1:实现带迭代器的链表
class LinkedListWithIterator:def __init__(self):self.head = Nonedef __iter__(self):current = self.headwhile current:yield current.valcurrent = 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特性,适合任务调度和缓冲场景
理解每种结构的时间复杂度特征和适用场景,能够帮助我们在实际编程中做出正确的选择。下一阶段我们将进入非线性数据结构的学习,包括树、堆、图等更复杂的数据结构。
