|
| 1 | +## 1.1 次短路径简介 |
| 2 | + |
| 3 | +> **次短路径**:给定一个带权有向图,求从起点到终点的次短路径。次短路径是指长度严格大于最短路径的所有路径中长度最小的那条路径。 |
| 4 | +
|
| 5 | +### 1.1.1 问题特点 |
| 6 | + |
| 7 | +- 次短路径必须严格大于最短路径 |
| 8 | +- 可能存在多条最短路径,但次短路径是唯一的 |
| 9 | +- 如果不存在次短路径(如最短路径是唯一的),则返回 $-1$。 |
| 10 | + |
| 11 | +### 1.1.2 常见变体 |
| 12 | + |
| 13 | +1. 允许重复边的次短路径 |
| 14 | +2. 不允许重复边的次短路径 |
| 15 | +3. 带约束条件的次短路径(如必须经过某些节点) |
| 16 | + |
| 17 | +## 1.2 次短路径基本思路 |
| 18 | + |
| 19 | +求解次短路径的常用方法是使用 Dijkstra 算法的变体。基本思路如下: |
| 20 | + |
| 21 | +1. 使用 Dijkstra 算法找到最短路径。 |
| 22 | +2. 在寻找最短路径的过程中,同时维护次短路径。 |
| 23 | +3. 对于每个节点,我们需要维护两个距离值: |
| 24 | + - $dist1[u]$:从起点到节点 u 的最短距离。 |
| 25 | + - $dist2[u]$:从起点到节点 u 的次短距离。 |
| 26 | + |
| 27 | +### 1.2.1 具体实现步骤 |
| 28 | + |
| 29 | +1. 初始化 $dist1$ 和 $dist2$ 数组,所有值设为无穷大。 |
| 30 | +2. 将起点加入优先队列,距离为 $0$。 |
| 31 | +3. 每次从队列中取出距离最小的节点 $u$。 |
| 32 | +4. 遍历 $u$ 的所有邻接节点 $v$: |
| 33 | + - 如果找到更短的路径,更新 $dist1[v]$。 |
| 34 | + - 如果找到次短的路径,更新 $dist2[v]$。 |
| 35 | +5. 最终 $dist2[终点]$ 即为所求的次短路径长度。 |
| 36 | + |
| 37 | +### 1.2.2 算法正确性证明 |
| 38 | + |
| 39 | +1. 对于任意节点 $u$,$dist1[u]$ 一定是最短路径长度。 |
| 40 | +2. 对于任意节点 $u$,$dist2[u]$ 一定是次短路径长度。 |
| 41 | +3. 算法会考虑所有可能的路径,因此不会遗漏次短路径。 |
| 42 | + |
| 43 | +## 1.3 次短路径代码实现 |
| 44 | + |
| 45 | +```python |
| 46 | +import heapq |
| 47 | + |
| 48 | +def second_shortest_path(n: int, edges: List[List[int]], start: int, end: int) -> int: |
| 49 | + """ |
| 50 | + 计算从起点到终点的次短路径长度 |
| 51 | + |
| 52 | + 参数: |
| 53 | + n: 节点数量 |
| 54 | + edges: 边列表,每个元素为 [起点, 终点, 权重] |
| 55 | + start: 起始节点 |
| 56 | + end: 目标节点 |
| 57 | + |
| 58 | + 返回: |
| 59 | + 次短路径的长度,如果不存在则返回 -1 |
| 60 | + """ |
| 61 | + # 构建邻接表 |
| 62 | + graph = [[] for _ in range(n)] |
| 63 | + for u, v, w in edges: |
| 64 | + graph[u].append((v, w)) |
| 65 | + |
| 66 | + # 初始化距离数组 |
| 67 | + dist1 = [float('inf')] * n # 最短距离 |
| 68 | + dist2 = [float('inf')] * n # 次短距离 |
| 69 | + dist1[start] = 0 |
| 70 | + |
| 71 | + # 优先队列,存储 (距离, 节点) 的元组 |
| 72 | + pq = [(0, start)] |
| 73 | + |
| 74 | + while pq: |
| 75 | + d, u = heapq.heappop(pq) |
| 76 | + |
| 77 | + # 如果当前距离大于次短距离,跳过 |
| 78 | + if d > dist2[u]: |
| 79 | + continue |
| 80 | + |
| 81 | + # 遍历所有邻接节点 |
| 82 | + for v, w in graph[u]: |
| 83 | + # 计算新的距离 |
| 84 | + new_dist = d + w |
| 85 | + |
| 86 | + # 如果找到更短的路径 |
| 87 | + if new_dist < dist1[v]: |
| 88 | + dist2[v] = dist1[v] # 原来的最短路径变成次短路径 |
| 89 | + dist1[v] = new_dist # 更新最短路径 |
| 90 | + heapq.heappush(pq, (new_dist, v)) |
| 91 | + # 如果找到次短的路径 |
| 92 | + elif new_dist > dist1[v] and new_dist < dist2[v]: |
| 93 | + dist2[v] = new_dist |
| 94 | + heapq.heappush(pq, (new_dist, v)) |
| 95 | + |
| 96 | + return dist2[end] if dist2[end] != float('inf') else -1 |
| 97 | + |
| 98 | +# 使用示例 |
| 99 | +n = 4 |
| 100 | +edges = [ |
| 101 | + [0, 1, 1], |
| 102 | + [1, 2, 2], |
| 103 | + [2, 3, 1], |
| 104 | + [0, 2, 4], |
| 105 | + [1, 3, 5] |
| 106 | +] |
| 107 | +start = 0 |
| 108 | +end = 3 |
| 109 | + |
| 110 | +result = second_shortest_path(n, edges, start, end) |
| 111 | +print(f"次短路径长度: {result}") |
| 112 | +``` |
| 113 | + |
| 114 | +## 1.4 算法复杂度分析 |
| 115 | + |
| 116 | +- 时间复杂度:$O((V + E)\log V)$,其中 $V$ 是节点数,$E$ 是边数。 |
| 117 | +- 空间复杂度:$O(V)$,用于存储距离数组和优先队列。 |
| 118 | + |
| 119 | +## 1.5 应用场景 |
| 120 | + |
| 121 | +1. 网络路由:寻找备用路径。 |
| 122 | +2. 交通规划:寻找替代路线。 |
| 123 | +3. 通信网络:寻找备用通信路径。 |
| 124 | +4. 物流配送:规划备用配送路线。 |
| 125 | + |
| 126 | +## 1.6 注意事项 |
| 127 | + |
| 128 | +1. 次短路径必须严格大于最短路径。 |
| 129 | +2. 如果不存在次短路径,返回 $-1$。 |
| 130 | +3. 图中可能存在负权边,此时需要使用 Bellman-Ford 算法的变体。 |
| 131 | +4. 对于无向图,需要将每条边都加入两次。 |
0 commit comments