1. Dijkstra 演算法 (Dijkstra’s Algorithm)
核心概念: 貪婪策略 (Greedy) + 優先佇列 (Priority Queue)。 適用場景: 非負權重圖的單源最短路徑 (Single-Source Shortest Path, SSSP)。 核心邏輯: 每次從「未訪問集合」中選取距離起點最近的節點,對其鄰居進行鬆弛 (Relaxation)。
- 時間複雜度: $O(E \log V)$ (使用 Min-Heap)
- 空間複雜度: $O(V + E)$
2. Bellman-Ford 演算法
核心概念: 動態規劃 / 暴力鬆弛。 適用場景: 含負權邊的單源最短路徑,可檢測負權環 (Negative Cycle)。 核心邏輯: 對所有邊進行 $V-1$ 次鬆弛操作。若第 $V$ 次仍能鬆弛,則存在負環。
- 時間複雜度: $O(VE)$
- 空間複雜度: $O(V + E)$
3. SPFA 演算法 (Shortest Path Faster Algorithm)
核心概念: Bellman-Ford 的佇列優化版本。 適用場景: 含負權邊但無負環的圖,且圖非稠密網格圖 (Grid Graph)。 核心邏輯: 只有當節點 $u$ 被鬆弛變小後,其鄰居才有可能變小。僅將被更新過且不在佇列中的節點加入佇列。
- 時間複雜度: 平均 $O(kE)$ ($k$ 為常數,通常很小),但在特製測資(稠密圖/網格圖)下會退化至 $O(VE)$。
- 注意: 現代競賽中容易被卡 (Time Limit Exceeded),非必要請優先使用 Dijkstra。
4. Floyd-Warshall 演算法
核心概念: 動態規劃 (Dynamic Programming)。 適用場景: 全對最短路徑 (All-Pairs Shortest Path, APSP),節點數少 ($V \le 500$)。 核心邏輯: $dp[k][i][j]$ 表示從 $i$ 到 $j$ 只經過前 $k$ 個節點的最短路徑。 狀態轉移方程:$dist[i][j] = \min(dist[i][j], dist[i][k] + dist[k][j])$。
- 時間複雜度: $O(V^3)$
- 空間複雜度: $O(V^2)$
總結比較表 (Comparison)
| 算法 | 類型 | 邊權限制 | 時間複雜度 | 適用情境 |
|---|---|---|---|---|
| Dijkstra | SSSP | 非負 | $O(E \log V)$ | 一般最短路首選,地圖導航。 |
| Bellman-Ford | SSSP | 可負 | $O(VE)$ | 需檢測負環或邊數極少時。 |
| SPFA | SSSP | 可負 | Avg $O(kE)$, Max $O(VE)$ | 隨機圖快,但在競賽中易被卡。 |
| Floyd-Warshall | APSP | 可負 | $O(V^3)$ | 點數少 ($N \le 500$) 的全對最短路。 |
import heapq
from copy import deepcopy
def dijkstra(graph: dict[int, list[tuple[int, int]]],
start: int) -> dict[int, int]:
dist = {i: float('inf') for i in graph}
dist[start] = 0
pq = [(0, start)]
while pq:
d, u = heapq.heappop(pq)
if d > dist[u]:
continue
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
heapq.heappush(pq, (dist[v], v))
return dist
def bellman_ford(graph: dict[int, list[tuple[int, int]]],
start: int) -> dict[int, int]:
dist = {i: float('inf') for i in graph}
dist[start] = 0
for _ in range(len(graph) - 1):
for u in graph:
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
return dist
def spfa(graph: dict[int, list[tuple[int, int]]], start: int) -> dict[int, int]:
dist = {i: float('inf') for i in graph}
dist[start] = 0
pq = [start]
while pq:
u = pq.pop(0)
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
pq.append(v)
return dist
def floyd_warshall(graph: list[list[int]]) -> list[list[int]]:
dist = deepcopy(graph)
for k in range(len(graph)):
for i in range(len(graph)):
for j in range(len(graph)):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
return dist