一、核心思想解析
Prim算法基于贪心策略,通过以下步骤构建最小生成树:
-
初始化阶段
- 任选起始顶点加入MST集合
- 创建候选边集合(连接已选与未选顶点的边)
-
迭代扩展过程
- 每次选择候选边中权重最小的边
- 将新顶点加入MST集合
- 更新候选边集合
-
终止条件
- 当所有顶点都加入MST时结束
- 最终边集构成最小生成树
二、算法关键特性
时间复杂度分析
- 基础实现(邻接矩阵):O(V²) → 适合稠密图
- 优先队列优化(邻接表):O(E log V) → 适合稀疏图
- 斐波那契堆优化:O(E + V log V) → 理论最优
空间复杂度
- 邻接表存储:O(V + E)
- 辅助数据结构:O(V)
适用场景
- 通信网络布线优化
- 交通枢纽连接规划
- 电力网络拓扑构建
- 物联网设备组网
核心限制
- 仅适用于带权无向连通图
- 无法处理负权边(需用其他算法)
三、代码实现示例
Python版本
import heapq
def prim(graph):
"""
使用 Prim 算法计算加权无向图的最小生成树
参数:
graph (dict): 邻接表表示的图,格式为 {节点: [(邻接节点, 边权重), ...]}
返回:
list: 最小生成树的边列表,每条边格式为 (权重, 节点1, 节点2)
"""
if not graph:
return []
# 选择任意节点作为起始点
start_node = next(iter(graph))
visited = {start_node}
edges = [] # 存储 MST 的边
heap = [] # 优先队列,存储候选边
# 初始化优先队列
for neighbor, weight in graph[start_node]:
heapq.heappush(heap, (weight, start_node, neighbor))
while heap and len(visited) < len(graph):
weight, u, v = heapq.heappop(heap)
# 如果目标节点已访问,则跳过
if v in visited:
continue
# 将边加入 MST,并标记节点为已访问
edges.append((weight, u, v))
visited.add(v)
# 将新节点的所有邻接边加入优先队列
for neighbor, w in graph[v]:
if neighbor not in visited:
heapq.heappush(heap, (w, v, neighbor))
return edges
C++版本
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_set>
#include <utility>
#include <string>
using namespace std;
// 定义边的结构,包含目标节点和权重
struct Edge {
string to;
int weight;
};
// 定义优先队列中的元素,包含权重、源节点和目标节点
struct QueueElement {
int weight;
string from;
string to;
// 重载比较运算符,使优先队列按权重从小到大排序
bool operator>(const QueueElement& other) const {
return weight > other.weight;
}
};
// Prim 算法实现
vector<pair<int, pair<string, string>>> prim(const unordered_map<string, vector<Edge>>& graph) {
vector<pair<int, pair<string, string>>> mst; // 存储 MST 的边
if (graph.empty()) return mst;
// 选择任意节点作为起始点
string start_node = graph.begin()->first;
unordered_set<string> visited;
visited.insert(start_node);
// 优先队列,存储候选边
priority_queue<QueueElement, vector<QueueElement>, greater<QueueElement>> heap;
// 初始化优先队列
for (const auto& edge : graph.at(start_node)) {
heap.push({edge.weight, start_node, edge.to});
}
while (!heap.empty() && visited.size() < graph.size()) {
QueueElement current = heap.top();
heap.pop();
// 如果目标节点已访问,则跳过
if (visited.find(current.to) != visited.end()) {
continue;
}
// 将边加入 MST,并标记节点为已访问
mst.push_back({current.weight, {current.from, current.to}});
visited.insert(current.to);
// 将新节点的所有邻接边加入优先队列
for (const auto& edge : graph.at(current.to)) {
if (visited.find(edge.to) == visited.end()) {
heap.push({edge.weight, current.to, edge.to});
}
}
}
return mst;
}
四、时间复杂度深度分析
基础实现分析
- 每次选择最小边需要O(V)时间
- 总迭代次数为V次
- 最终复杂度:O(V²)
- 最佳场景:完全图或邻接矩阵存储
优先队列优化
- 堆作成本:每次插入/删除O(log E)
- 边处理次数:最多E次
- 最终复杂度:O(E log E) → 可优化为O(E log V)
- 优势场景:稀疏图(E << V²)
斐波那契堆优化
- 降低减少键作成本至O(1)
- 理论最优复杂度:O(E + V log V)
- 实际应用:超大规模图处
五、总结
- Python 版本:使用
heapq
库实现优先队列,通过字典和元组表示图结构与边,简洁直观。 - C++ 版本:利用
priority_queue
和unordered_map
,结合自定义结构体和比较运算符,实现高效的优先队列与图存储。
Prim 算法通过贪心策略平衡效率与正确性,在不同场景下可通过优化数据结构(如优先队列、斐波那契堆)提升性能。其在网络拓扑优化、资源分配等领域具有广泛应用,是图论算法中的基础且重要的工具。