在 Day 26 我們介紹了 最小生成樹 (MST) 的概念,並且在 Day 27 深入探討了 Kruskal 演算法。今天要介紹的是另一個經典方法 —— Prim 演算法。它與 Kruskal 不同,Prim 是以 節點為導向 的最小生成樹演算法。
Prim 演算法的核心思想是: 從某個節點開始,每次選取一條 最小權重的邊,將新的節點加入生成樹,直到所有節點都被包含。
這是一種貪心演算法 (Greedy Algorithm)。它與 Dijkstra 有相似之處: 都是透過優先佇列 (Min-Heap) 逐步擴展搜尋範圍。
import heapq
def prim(graph, start=0):
"""
graph: adjacency list 格式 {u: [(v, w), ...]}
start: 起點
"""
V = len(graph)
visited = [False] * V
mst = []
pq = [(0, start, -1)] # (權重, 當前節點, 來源節點)
while pq:
w, u, parent = heapq.heappop(pq)
if visited[u]:
continue
visited[u] = True
if parent != -1:
mst.append((parent, u, w))
for v, weight in graph[u]:
if not visited[v]:
heapq.heappush(pq, (weight, v, u))
return mst
# 測試
graph = {
0: [(1, 10), (2, 6), (3, 5)],
1: [(0, 10), (3, 15)],
2: [(0, 6), (3, 4)],
3: [(0, 5), (1, 15), (2, 4)]
}
mst = prim(graph, 0)
print("Prim MST:", mst)
# 輸出: [(0, 3, 5), (2, 3, 4), (0, 1, 10)]
Prim 特別適合稠密圖 (Dense Graph),因為它會不斷操作鄰接邊集合
特性 | Kruskal | Prim |
---|---|---|
導向 | 邊導向 (Edge-based) | 點導向 (Vertex-based) |
工具 | 並查集 (Union-Find) | 優先佇列 (Heap) |
複雜度 | $O(E \log E)$ | $O(E \log V)$ |
適合圖 | 稀疏圖 (Sparse Graph) | 稠密圖 (Dense Graph) |
建構方式 | 每次選邊,避免成環 | 從一個點擴展整棵樹 |
Prim 與 Kruskal 雖然思路不同,但它們的目標都是構造出 最小生成樹。
透過這兩個演算法的比較,我們不僅理解了 MST 的本質,也能根據不同場景靈活選擇方法