Python学习算法系列(9):图论算法与搜索算法
1. 引言
图论是研究图的性质和应用的数学理论,图由节点(Vertex)和边(Edge)组成。在计算机科学中,图论广泛应用于网络、路径寻找、社会网络分析等领域。图论的算法涉及多种常见问题,例如最短路径、图的遍历、拓扑排序等。
本篇博客将介绍:
- 图的基本概念
- 图的遍历算法(深度优先搜索与广度优先搜索)
- 最短路径算法(Dijkstra与Bellman-Ford)
- 拓扑排序
2. 图的基本概念
在图论中,图通常分为以下几种类型:
- 无向图:图中的边没有方向。
- 有向图:图中的边有方向。
- 加权图:图中的每条边都有一个权重值。
- 无权图:图中的边没有权重。
图的表示方式:
- 邻接矩阵:使用一个二维矩阵来表示图,适合稠密图。
- 邻接表:使用链表或数组存储每个节点的邻接节点,适合稀疏图。
📌 示例:无向图的邻接表表示
graph = {
0: [1, 2], # 节点0连接节点1和2
1: [0, 2], # 节点1连接节点0和2
2: [0, 1], # 节点2连接节点0和1
}
3. 图的遍历算法
图的遍历是指访问图中所有节点并且按一定的顺序访问每个节点。图的遍历算法主要有两种:
- 深度优先搜索(DFS)
- 广度优先搜索(BFS)
3.1 深度优先搜索(DFS)
深度优先搜索(DFS)是通过递归或栈的方式,深入到图的每一个分支,直到没有未访问的邻节点,然后回溯。
def dfs(graph, start, visited=None):
if visited is None:
visited = set()
visited.add(start)
print(start, end=" ") # 访问节点
for neighbor in graph[start]:
if neighbor not in visited:
dfs(graph, neighbor, visited)
# 测试DFS
graph = {
0: [1, 2],
1: [0, 2],
2: [0, 1]
}
dfs(graph, 0) # 输出:0 1 2
解释
- 从起始节点
0开始,DFS 会访问所有节点。 - 每访问一个节点时,递归地继续访问它的邻接节点。
3.2 广度优先搜索(BFS)
广度优先搜索(BFS)是一种通过队列(queue)实现的遍历算法,它先访问起始节点的邻节点,再访问邻节点的邻节点,依此类推。
from collections import deque
def bfs(graph, start):
visited = set()
queue = deque([start])
while queue:
vertex = queue.popleft()
if vertex not in visited:
visited.add(vertex)
print(vertex, end=" ") # 访问节点
queue.extend(neighbor for neighbor in graph[vertex] if neighbor not in visited)
# 测试BFS
bfs(graph, 0) # 输出:0 1 2
解释
- 从起始节点
0开始,BFS 会先访问所有与0直接相连的节点,然后依次访问每个节点的邻节点。
4. 最短路径算法
4.1 Dijkstra 算法
Dijkstra 算法用于求解有向图或无向图中从一个节点到所有其他节点的最短路径。该算法的基本思想是每次选取当前最短的路径并更新邻接节点的路径长度。
import heapq
def dijkstra(graph, start):
# 初始化最短路径
distances = {node: float('inf') for node in graph}
distances[start] = 0
priority_queue = [(0, start)]
while priority_queue:
current_distance, current_node = heapq.heappop(priority_queue)
if current_distance > distances[current_node]:
continue
for neighbor, weight in graph[current_node]:
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(priority_queue, (distance, neighbor))
return distances
# 测试Dijkstra
graph = {
'A': [('B', 1), ('C', 4)],
'B': [('A', 1), ('C', 2), ('D', 5)],
'C': [('A', 4), ('B', 2), ('D', 1)],
'D': [('B', 5), ('C', 1)],
}
print(dijkstra(graph, 'A')) # 输出:{'A': 0, 'B': 1, 'C': 3, 'D': 4}
解释
distances保存从起始节点到其他节点的最短距离。- 每次从
priority_queue中取出距离最小的节点进行更新。
4.2 Bellman-Ford 算法
Bellman-Ford 算法是另一种解决单源最短路径问题的算法,适用于带有负权边的图。它通过松弛操作,反复更新所有边的最短路径,直到没有更新为止。
def bellman_ford(graph, start):
distances = {node: float('inf') for node in graph}
distances[start] = 0
# 对所有边进行V-1轮松弛操作
for _ in range(len(graph) - 1):
for u in graph:
for v, weight in graph[u]:
if distances[u] + weight < distances[v]:
distances[v] = distances[u] + weight
return distances
# 测试Bellman-Ford
graph = {
'A': [('B', 1), ('C', 4)],
'B': [('A', 1), ('C', 2), ('D', 5)],
'C': [('A', 4), ('B', 2), ('D', 1)],
'D': [('B', 5), ('C', 1)],
}
print(bellman_ford(graph, 'A')) # 输出:{'A': 0, 'B': 1, 'C': 3, 'D': 4}
5. 拓扑排序
拓扑排序用于有向无环图(DAG)的排序,它可以有效地为任务调度、依赖分析等问题提供解决方案。
from collections import defaultdict, deque
def topological_sort(graph):
in_degree = defaultdict(int)
for node in graph:
for neighbor in graph[node]:
in_degree[neighbor] += 1
queue = deque([node for node in graph if in_degree[node] == 0])
result = []
while queue:
node = queue.popleft()
result.append(node)
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
if len(result) == len(graph):
return result
else:
raise ValueError("图中有环,无法进行拓扑排序")
# 测试拓扑排序
graph = {
'A': ['B', 'C'],
'B': ['D'],
'C': ['D'],
'D': []
}
print(topological_sort(graph)) # 输出:['A', 'B', 'C', 'D']
解释
in_degree记录每个节点的入度,入度为 0 的节点可以首先访问。- 通过队列管理节点的拓扑顺序,直到所有节点都被处理。
6. 总结
✅ 图论算法:
- 图的表示:邻接矩阵与邻接表。
- 图的遍历:深度优先搜索(DFS)与广度优先搜索(BFS)。
- 最短路径算法:Dijkstra 和 Bellman-Ford。
- 拓扑排序:有向无环图(DAG)的排序方法。
📢 下一篇 Python学习算法系列(10):动态规划与贪心算法,敬请期待!🚀
💡 如果你喜欢这篇文章,欢迎点赞、收藏,并关注本系列!
1762

被折叠的 条评论
为什么被折叠?



