链表是一种线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。在 Python 中,通常使用类来实现链表节点。
数据的动态插入与删除:在一些需要频繁插入和删除元素的场景中,链表比数组更高效。例如,在模拟队列或栈的动态操作时,如果使用数组,插入和删除操作可能涉及大量元素的移动,而链表只需修改指针。
实现其他数据结构:链表可以作为基础结构来实现栈、队列等。
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
# 合并两个有序链表
def mergeTwoLists(l1, l2):
dummy = ListNode(0)
current = dummy
while l1 and l2:
if l1.val < l2.val:
current.next = l1
l1 = l1.next
else:
current.next = l2
l2 = l2.next
current = current.next
if l1:
current.next = l1
if l2:
current.next = l2
return dummy.next
栈是一种后进先出(LIFO)的数据结构,只能在栈顶进行插入(入栈)和删除(出栈)操作。在 Python 中,可以使用列表来模拟栈。
括号匹配问题:检查表达式中的括号是否匹配。遍历表达式,遇到左括号入栈,遇到右括号时,从栈中弹出一个左括号进行匹配。
表达式求值:如逆波兰表达式求值,利用栈来处理运算符和操作数。
函数调用栈:在递归调用中,系统会使用栈来保存函数的调用信息。
# 括号匹配
def isValid(s):
stack = []
mapping = {')': '(', ']': '[', '}': '{'}
for char in s:
if char in mapping:
if not stack or stack.pop() != mapping[char]:
return False
else:
stack.append(char)
return not stack
队列是一种先进先出(FIFO)的数据结构,元素从队尾插入(入队),从队头删除(出队)。在 Python 中,可以使用 collections.deque
来实现队列。
广度优先搜索(BFS):在图的遍历、迷宫问题等场景中,使用队列来存储待访问的节点,确保按照层次顺序进行访问。
任务调度:例如操作系统中的任务队列,新任务加入队尾,处理程序从队头取出任务进行处理。
from collections import deque
# 广度优先搜索示例
def bfs(graph, start):
visited = set()
queue = deque([start])
visited.add(start)
while queue:
vertex = queue.popleft()
print(vertex, end=" ")
for neighbor in graph[vertex]:
if neighbor not in visited:
queue.append(neighbor)
visited.add(neighbor)
堆是一种完全二叉树,分为最大堆和最小堆。最大堆中,每个节点的值都大于或等于其子节点的值;最小堆中,每个节点的值都小于或等于其子节点的值。在 Python 中,可以使用 heapq
模块来实现最小堆。
优先队列:堆可以用来实现优先队列,元素按照优先级进行排序,优先级高的元素先出队。
Top K 问题:找出数据集中最大或最小的 K 个元素。
合并 K 个有序列表:利用最小堆可以高效地合并多个有序列表。
import heapq
# 合并 K 个有序列表
def mergeKLists(lists):
dummy = ListNode(0)
current = dummy
heap = []
for i, lst in enumerate(lists):
if lst:
heapq.heappush(heap, (lst.val, i, lst))
while heap:
val, index, node = heapq.heappop(heap)
current.next = node
current = current.next
if node.next:
heapq.heappush(heap, (node.next.val, index, node.next))
return dummy.next
ST 表(Sparse Table)是一种用于解决静态区间最值问题(RMQ)的数据结构,它可以在 (O(n log n)) 的时间复杂度内进行预处理,然后在 (O(1)) 的时间复杂度内查询任意区间的最值。
区间最值查询:在一些需要频繁查询区间最值的问题中,ST 表可以显著提高查询效率。
import math
# ST 表求解区间最大值
def build_st(arr):
n = len(arr)
k = int(math.log2(n)) + 1
st = [[0] * k for _ in range(n)]
for i in range(n):
st[i][0] = arr[i]
for j in range(1, k):
for i in range(n - (1 << j) + 1):
st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1])
return st
def query_max(st, l, r):
j = int(math.log2(r - l + 1))
return max(st[l][j], st[r - (1 << j) + 1][j])
并查集(Union-Find)是一种用于处理不相交集合的合并与查询问题的数据结构。它支持两种操作:合并两个集合(Union)和查询某个元素所属的集合(Find)。
连通性问题:在图中判断两个节点是否连通,或者将多个连通分量合并。
朋友圈问题:判断哪些人属于同一个朋友圈。
# 并查集
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
root_x = self.find(x)
root_y = self.find(y)
if root_x != root_y:
if self.rank[root_x] > self.rank[root_y]:
self.parent[root_y] = root_x
elif self.rank[root_x] < self.rank[root_y]:
self.parent[root_x] = root_y
else:
self.parent[root_y] = root_x
self.rank[root_x] += 1
可撤销并查集是在普通并查集的基础上,支持撤销之前的合并操作。通常使用栈来记录每次合并的信息,以便在需要时进行撤销。
回滚操作:在一些动态问题中,可能需要撤销之前的操作,可撤销并查集可以满足这种需求。
带权并查集是在普通并查集的基础上,每个节点额外维护一个权值,用于记录节点与根节点之间的某种关系。在合并和查询操作中,需要同时更新权值。
种类并查集:用于处理元素之间存在多种关系的问题,如食物链问题,每个节点的权值表示其与根节点的关系类型。
距离并查集:在一些图的问题中,节点的权值可以表示其到根节点的距离。
# 带权并查集(以食物链问题为例)
class WeightedUnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
self.weight = [0] * n
def find(self, x):
if self.parent[x] != x:
root = self.find(self.parent[x])
self.weight[x] += self.weight[self.parent[x]]
self.parent[x] = root
return self.parent[x]
def union(self, x, y, d):
root_x = self.find(x)
root_y = self.find(y)
if root_x != root_y:
self.parent[root_x] = root_y
self.weight[root_x] = self.weight[y] - self.weight[x] + d
这些数据结构在蓝桥杯的题目中经常会用到,通过掌握它们的基本原理和应用场景,可以帮助你更好地解决各种问题。建议你多做一些相关的练习题,加深对这些数据结构的理解和应用能力。