📚 由淺至深
第 14 章 進階 約 18 分鐘

Dijkstra:Google Maps 怎麼幫你算最短路

有權重的圖找最短路徑,BFS 不夠用。Dijkstra 像水從起點以「實際距離」擴散。

建議先看過: 13-bfs-dfs

BFS 為什麼不夠?

BFS 找最短路是按「邊的數量」算的。但 Google Maps 不是這樣—— 走 1 公里高速公路 vs 走 5 公里小路,前者更快。

真實世界的邊有「成本」(權重):距離、時間、過路費。 這時要用 Dijkstra

核心想法

像 BFS 一樣從起點擴散,但不是按步數,而是按「累計距離」。

每次從「目前已知最近的未拜訪點」出發更新它的鄰居。

來看一遍

從 A 出發找到所有點的最短距離:

42351ABCD

維護一個「目前已知最短距離表」,初始時除起點外都是 ∞:

ABCD已處理
初始0{}
處理 A042{A}
處理 C(最小,距離 2)0min(4, 2+3)=422+1=3{A,C}
處理 D(距離 3)0min(4, 3+5)=423{A,C,D}
處理 B(距離 4)0423{A,B,C,D} ✓

結果:A→A=0, A→B=4, A→C=2, A→D=3。

💡關鍵步驟

每次從未處理的點裡挑距離最小的處理。 處理 X 時,更新 X 所有鄰居的距離dist[鄰居] = min(舊距離, dist[X] + 邊權重) 這個更新叫 Relaxation(鬆弛)

Pseudocode

Algorithm Dijkstra(graph, source):
dist[v] ← ∞ for v in V
dist[source] ← 0
pq ← PriorityQueue() // 按距離排
pq.push((0, source))
while pq not empty:
(d, u) ← pq.pop() // 最小距離的點
if d > dist[u]: continue // 過時項
for (v, w) in graph[u]: // v 是鄰居,w 是邊權重
if dist[u] + w < dist[v]:
dist[v] ← dist[u] + w
pq.push((dist[v], v))

複雜度

實作時間
用陣列找最小O(V²)
用 Priority Queue(heap)O((V+E) log V) ← 實務版
⚠️Dijkstra 的限制

邊權重不能是負數! 為什麼?Dijkstra 假設「一旦處理某個點,它的最短距離就確定了」。 但負邊會讓「後來才發現的路徑更短」,違反假設。

有負邊要用 Bellman-Ford 演算法(這個比較進階,先記得名字就好)。

📝考試會這樣考

題型一:給帶權圖,列出 Dijkstra 每一輪的距離表。

訣竅:畫表格,每輪標清楚「處理哪個點、更新了哪些距離」。 每一步都要寫出來,老師才能給分。

題型二:為什麼 Dijkstra 不適用於負邊? 舉反例:A →(1)→ B →(-3)→ C,A 到 C 直接走 A→C 算成 ∞(沒這條邊), 但繞 A→B→C = -2 才是最短。Dijkstra 會卡死。

題型三:Dijkstra vs BFS 差別。

  • BFS 適用無權重(或所有邊權重相同)
  • Dijkstra 適用非負權重
  • 兩者都是「從起點擴散」,但「最近」的定義不同。

這章記住

  1. 每次處理「目前最近的未拜訪點」,更新它鄰居的距離。
  2. 需要 Priority Queue(min-heap) 加速。
  3. 不能有負邊——有負邊用 Bellman-Ford。
  4. 時間 O((V+E) log V),是 Google Maps、網路路由協定的核心。

學習進度只存在你的瀏覽器,不會上傳。