diff --git a/subjects/major/图论常见算法.md b/subjects/major/图论常见算法.md index 36e6b8d..d974685 100644 --- a/subjects/major/图论常见算法.md +++ b/subjects/major/图论常见算法.md @@ -12,4 +12,210 @@ ### Bellman-Ford算法 -### Bellman-Ford算法 \ No newline at end of file +#### 数学公式 + +对于图 $G=(V,E)$,Bellman-Ford算法通过迭代松弛更新每个顶点的最短路径估计: + +$$d(v) = \min\{d(u) + w(u,v) | (u,v) \in E\}$$ + +其中: +- $d(v)$ 是从源顶点到顶点 $v$ 的最短路径估计 +- $w(u,v)$ 是边 $(u,v)$ 的权重 +- 迭代次数最多为 $|V|-1$ 次 + +#### Python实现 + +```python +def bellman_ford(graph, source): + """ + Bellman-Ford算法求单源最短路径 + + 参数: + graph: 邻接表表示的图,格式 {u: [(v, w), ...]} + source: 源顶点 + + 返回: + distances: 各顶点到源顶点的最短距离 + """ + # 初始化距离 + distances = {v: float('inf') for v in graph} + distances[source] = 0 + + # 获取所有顶点 + vertices = list(graph.keys()) + + # 最多进行 |V|-1 次迭代 + for _ in range(len(vertices) - 1): + # 对每条边进行松弛操作 + for u in graph: + for v, w in graph[u]: + if distances[u] != float('inf') and distances[u] + w < distances[v]: + distances[v] = distances[u] + w + + # 检测负权重环 + for u in graph: + for v, w in graph[u]: + if distances[u] != float('inf') and distances[u] + w < distances[v]: + raise ValueError("图中存在负权重环") + + return distances + + +# 示例使用 +if __name__ == "__main__": + # 构建图 (邻接表) + graph = { + 'A': [('B', 4), ('C', 2)], + 'B': [('C', 3), ('D', 2), ('E', 3)], + 'C': [('B', 1), ('D', 4)], + 'D': [('E', -5)], # 负权重边 + 'E': [] + } + + try: + distances = bellman_ford(graph, 'A') + print("Bellman-Ford 结果:", distances) + except ValueError as e: + print(e) +``` + +--- + +## 常用算法Python实现 + +### 1. Dijkstra算法 + +#### 数学公式 + +Dijkstra算法维护一个优先队列,每次选取具有最小距离估计的顶点: + +$$d(v) = \min_{u \in S} \{ d(u) + w(u,v) \}$$ + +其中 $S$ 是已确定最短路径的顶点集合。 + +#### Python实现 + +```python +import heapq + +def dijkstra(graph, source): + """ + Dijkstra算法求单源最短路径 + + 参数: + graph: 邻接表表示的图,格式 {u: [(v, w), ...]} + source: 源顶点 + + 返回: + distances: 各顶点到源顶点的最短距离 + """ + # 初始化距离 + distances = {v: float('inf') for v in graph} + distances[source] = 0 + + # 优先队列: (距离, 顶点) + pq = [(0, source)] + visited = set() + + while pq: + # 取出最小距离的顶点 + current_dist, u = heapq.heappop(pq) + + # 如果已访问则跳过 + if u in visited: + continue + visited.add(u) + + # 更新邻接顶点的距离 + for v, w in graph.get(u, []): + if v not in visited and current_dist + w < distances[v]: + distances[v] = current_dist + w + heapq.heappush(pq, (distances[v], v)) + + return distances + + +# 示例使用 +if __name__ == "__main__": + # 构建图 (邻接表) + graph = { + 'A': [('B', 4), ('C', 2)], + 'B': [('C', 3), ('D', 2), ('E', 3)], + 'C': [('B', 1), ('D', 4)], + 'D': [('E', 5)], + 'E': [] + } + + distances = dijkstra(graph, 'A') + print("Dijkstra 结果:", distances) + # 输出: {'A': 0, 'B': 3, 'C': 2, 'D': 5, 'E': 8} +``` + +### 2. Bellman-Ford算法 + +#### 数学公式 + +对于图 $G=(V,E)$,Bellman-Ford算法通过迭代松弛更新每个顶点的最短路径估计: + +$$d(v) = \min\{d(u) + w(u,v) | (u,v) \in E\}$$ + +其中: +- $d(v)$ 是从源顶点到顶点 $v$ 的最短路径估计 +- $w(u,v)$ 是边 $(u,v)$ 的权重 +- 迭代次数最多为 $|V|-1$ 次 + +#### Python实现 + +```python +def bellman_ford(graph, source): + """ + Bellman-Ford算法求单源最短路径 + + 参数: + graph: 邻接表表示的图,格式 {u: [(v, w), ...]} + source: 源顶点 + + 返回: + distances: 各顶点到源顶点的最短距离 + """ + # 初始化距离 + distances = {v: float('inf') for v in graph} + distances[source] = 0 + + # 获取所有顶点 + vertices = list(graph.keys()) + + # 最多进行 |V|-1 次迭代 + for _ in range(len(vertices) - 1): + # 对每条边进行松弛操作 + for u in graph: + for v, w in graph[u]: + if distances[u] != float('inf') and distances[u] + w < distances[v]: + distances[v] = distances[u] + w + + # 检测负权重环 + for u in graph: + for v, w in graph[u]: + if distances[u] != float('inf') and distances[u] + w < distances[v]: + raise ValueError("图中存在负权重环") + + return distances + + +# 示例使用 +if __name__ == "__main__": + # 构建图 (邻接表) + graph = { + 'A': [('B', 4), ('C', 2)], + 'B': [('C', 3), ('D', 2), ('E', 3)], + 'C': [('B', 1), ('D', 4)], + 'D': [('E', -5)], # 负权重边 + 'E': [] + } + + try: + distances = bellman_ford(graph, 'A') + print("Bellman-Ford 结果:", distances) + except ValueError as e: + print(e) +```