Menu
CoddyTech

Prim 算法

最后更新

Prim 算法用来构建最小生成树(MST),也就是连接所有节点、没有环、总权重最小的一组边。它从一个起始节点出发,让一棵树向外生长。每一步都查看所有从树内连到树外节点的边,并加入其中最便宜的一条。点击上方的“播放”,观看这棵树如何不断扩展,每次都选权重最小、能到达新节点的边。

因为每次都选最小的跨越边,所以每次加入都是安全的(必定属于某棵最小生成树)。用二叉堆实现优先队列,以每个树外节点的最便宜边作为键,Prim 算法的复杂度是 O(E log V)。它与 Kruskal 算法不同:Kruskal 对所有边做全局排序,而不是让一棵连通的树逐步生长。

时间复杂度与空间复杂度

实现方式复杂度说明
二叉堆O(E log V)跨越边的优先队列
邻接矩阵O(V²)更简单,适合稠密图
空间O(V + E)树成员标记 + 优先队列
最适合稠密图从单个起始节点开始生长

分步说明

步骤发生了什么
1任选一个节点作为树的起点。
2查看所有从树内连到树外节点的边。
3选出权重最小的跨越边。
4把这条边和它连接的新节点加入树中。
5重复以上步骤,直到所有节点都在树中。

实例演示

为一个 4 节点的图构建 MST,边为 A-B=1、A-C=3、B-C=2、B-D=4、C-D=5,从 A 开始:

步骤树跨越边选中的边
1{A}A-B=1, A-C=3A-B(权重 1)
2{A, B}A-C=3, B-C=2, B-D=4B-C(权重 2)
3{A, B, C}B-D=4, C-D=5B-D(权重 4)
4{A, B, C, D}无:所有节点都已在树中完成:MST 总权重 1+2+4 = 7

什么时候用 Prim 算法

适合使用不适合使用
你需要求连通的无向带权图的最小生成树。图是有向的,或者你要求最短路径:这时改用 Dijkstra 或 Bellman-Ford。
图是稠密的(E 接近 V²);O(V²) 的矩阵写法简单又快。图是稀疏的,而且边已经排好序或容易排序:Kruskal 往往更简单。
你希望树从某个区域向外生长(例如增量式网络布局)。图不连通:Prim 只能覆盖一个连通分量,而你需要的是最小生成森林。
你手头已经有邻接结构和优先队列。你需要在全局边集上检测环:并查集(Kruskal)更符合这种结构。

Prim's Algorithm 代码

简洁、可运行的 Prim's Algorithm 实现,提供 Python, JavaScript, Java, C++, C 版本。选择一种语言,复制代码,或在 Coddy 在线编辑器中直接打开。

Prim's Algorithm 的 Python 代码

Python
1import heapq2
3
4def prim(graph, start):5    visited = {start}6    heap = [(w, start, v) for v, w in graph[start]]7    heapq.heapify(heap)8    mst, total = [], 09    while heap and len(visited) < len(graph):10        w, u, v = heapq.heappop(heap)11        if v in visited:12            continue13        visited.add(v)14        mst.append((u, v, w))15        total += w16        # Offer the new node's edges to the frontier17        for neighbor, weight in graph[v]:18            if neighbor not in visited:19                heapq.heappush(heap, (weight, v, neighbor))20    return mst, total21
22
23graph = {24    "A": [("B", 4), ("C", 1)],25    "B": [("A", 4), ("C", 3), ("D", 2)],26    "C": [("A", 1), ("B", 3), ("D", 5)],27    "D": [("B", 2), ("C", 5), ("E", 7)],28    "E": [("D", 7)],29}30
31mst, total = prim(graph, "A")32for u, v, w in mst:33    print(f"{u} - {v} (weight {w})")34print("Total MST weight:", total)
在 Python 在线编辑器中运行这段代码

Prim 算法常见问题

Prim 算法的时间复杂度是多少?
使用二叉堆实现优先队列时,Prim 算法的复杂度是 O(E log V)。更简单的邻接矩阵版本每一步都扫描一遍寻找最小跨越边,复杂度是 O(V²),在稠密图上可能更快。两者的空间复杂度都是 O(V + E)。
Prim 算法和 Kruskal 算法有什么区别?

两者都用贪心策略求最小生成树。Prim 从起始节点出发生长出一棵连通的树,借助优先队列反复加入离开这棵树的最便宜的边。Kruskal 把所有边按权重排序,借助并查集加入不会形成环的最便宜的边。稠密图上通常优先用 Prim,稀疏图上优先用 Kruskal。

Prim 算法一定能找到最优的 MST 吗?

能。每一步中跨越当前树的最便宜的边都可以安全加入,它一定属于某棵最小生成树(切分性质)。只要图是连通的,反复加入安全边就能得到真正的最小生成树。

什么时候该用 Prim 算法而不是 Kruskal 算法?
图很稠密时选 Prim,因为它 O(V²) 的邻接矩阵写法不需要对全部 E 条边排序,只需从起始节点生长出一棵树。Kruskal 在稀疏图上表现出色,这时对边列表排序的开销很低,并查集也能让环检测保持高效。两者都能得到正确的 MST,所以选择主要取决于边的稠密程度,以及你手头已有的数据结构。
起始节点会影响得到的 MST 吗?

不会。无论从哪个节点开始,MST 的总权重都相同,因为连通图的最小生成树权重是唯一的。只有当多条边权重相同、存在多棵 MST 时,选中的具体边才可能不同。除此之外,不管从哪个节点开始,Prim 都会得到完全相同的树。

Prim 算法能处理不连通的图吗?

不能直接处理。Prim 只生长一棵树,所以只能覆盖起始节点所在的连通分量,其余部分不会被触及。要覆盖每个连通分量,需要反复从未访问的节点重新运行 Prim,最终得到的是最小生成森林,而不是一棵树。

Coddy 编程语言插图

在 Coddy 掌握算法

开始