【算法4】4.3.最小生成树-Prim算法
最小生成树
加权图是一种为每条边关联一个权值或是成本的图模型。
最小生成树:图的生成树是它的一棵含有其所有顶点的无环连通子图。
一幅加权图的最小生成树(MST)是它的一棵权值(树中所有边的权值之和)最小的生成树。
计算最小生成树的两种经典算法:
- Prim 算法
- Kruskal 算法
抽象数据类型
public class Edge implements Comparable<Edge> {
Edge(int v, int w, double weight); // 构造器
double weight(); // 边的权重
int either(); // 边两端的顶点之一
int other(int v); // 另一个顶点
int compareTo(Edge that);
String toString();
}
public class EdgeWeightedGraph {
EdgeWeightedGraph(int V); // 创建一幅含有 V 个顶点的空图
EdgeWeightedGraph(In in); // 从输入流读取图
int V(); // 顶点数
int E(); // 边数
void addEdge(Edge e); // 添加一条边
Iterable<Edge> adj(int v); // 邻接边
Iterable<Edge> edges(); // 所有边
String toString();
}
public class MST {
MST(EdgeWeightedGraph G);
Iterable<Edge> edges(); // 最小生成树的所有边
double weight(); // 最小生成树的权重
}
Prim 算法
Prim 又叫普里姆算法,其应用了贪心算法思想,即每次选择权重最小的横切边加入树中。
思想:Prim 算法的每一步都会为一棵生长中的树添加一条边。
一开始这棵树只有一个顶点,然后会向它添加 V-1 条边,每次总是将下一条连接树中的顶点与不在树中的顶点且权重最小的边加入树中(即由树种的顶点所定义的切分中的一条横切边)。
/**
* 最小生成树 Prime 算法的延迟实现(失效的边是延迟删除的)
* 1.使用布尔数组标记最小生成树的顶点
* 2.使用队列保存最小生成树的边
* 3.使用优先队列保存横切边
* */
public class LazyPrimMST {
private boolean[] marked; // 最小生成树的顶点
private Queue<Edge> mst; // 最小生成树的边
private MinPQ<Edge> pq; // 横切边(包括失效的边)
public LazyPrimMST(EdgeWeightedGraph G) {
marked = new boolean[G.V()];
mst = new Queue<>();
pq = new MinPQ<>();
// 起点
visit(G, 0);
// Prim 算法
while (!pq.isEmpty()) {
Edge e = pq.remove();
int v = e.either();
int w = e.other(v);
if (marked[v] && marked[w]) continue; // 跳过失效的边
mst.add(e);
if (!marked[v]) visit(G, v);
if (!marked[w]) visit(G, w);
}
}
private void visit(EdgeWeightedGraph G, int v) {
marked[v] = true;
for (Edge e : G.adj(v)) {
if (!marked[e.other(v)]) pq.add(e);
}
}
public Iterable<Edge> edges() {
return mst;
}
public double weight() {
double weight = 0.0;
for (Edge e : mst) {
weight += e.weight();
}
return weight;
}
}