最小生成树MST API
public class MST
| 返回类型 | 方法 | 描述 | 
|---|---|---|
| MST(EdgeWeightedGraph G) | 构造函数 | |
| Iterable | edges() | MST所有边 | 
| double | weight() | MST权重 | 
Prim算法
- 描述:每一步为树添加一个边.开始树只有一个顶点,向其中添加V-1条边,每次从连接树中顶点和树补的边中选择权重最小的边
 - 命题:Prim算法可以得到任意加权连通图的最小生成树
 
证明:即切分定理
- 数据结构:
- 顶点:使用boolean marked[],当顶点v在树中,marked[v]==true
 - 边:Queue mst,保存MST中的边
 - 横切边:使用优先队列MinPQ根据权重比较所有边
- 边的失效:当marked[v]&&marked[w]时,这样的边已经非横切边,即失效
 
 
 
实现1.延时实现
- 延时实现:无效边留在优先队列pq当中,delMin()时判断,跳过无效边
 - 注意:LazyPrimMST的pq非即时删除无效边,则运行中pq的元素数会超过G.V(),因此MinPQ需要resize()
 

import edu.princeton.cs.algs4.In;import edu.princeton.cs.algs4.StdOut;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<>(G.V());visit(G,0);while (!pq.isEmpty()){Edge e = pq.delMin();//最小权边(可能无效)int v = e.either(), w = e.other(v);if (marked[v] && marked[w]) continue;mst.enqueue(e);if (!marked[w]) visit(G,w);//将v或w加入树中(另一个已经在树中)if (!marked[v]) visit(G,v);}}//标记入树函数private void visit(EdgeWeightedGraph G, int v){marked[v] = true;for (Edge e: G.adj(v))if (!marked[e.other(v)]) pq.insert(e);}public Iterable<Edge> edges(){ return mst;}public static void main(String[] args){In in = new In(args[0]);EdgeWeightedGraph G = new EdgeWeightedGraph(in);LazyPrimMST mst = new LazyPrimMST(G);for (Edge e:mst.edges())StdOut.println(e);}}
算法分析
- 命题:LazyPrim计算连通加权无向图G(V,E)的最小生成树所需空间和E成正比,时间和ElogE(最坏)成正比
 
证明:优先队列的insert()和delMin()中的比较是主要考虑部分.优先队列中E条边,即空间上限,最坏情况下,insert()为~lgE,delMin()为2lgE,因为最多只能插入E条边,删除E次最小元素.则最坏为ElogE(优先队列)
实现2.即时实现
- 即时实现:在pq中实时的删去无效边,只在pq中保存每个非树顶点w的一条边:即已知将其与树中点连接的最小权重边
 - PrimMST使用索引优先队列,edgeTo[]和distTo[],具有以下性质
- marked[i],i在树中
 - pq为索引优先队列,delMin()返回indexOfMin,即返回最小横切边关联的结点
 - edgeTo[v]是v与树相连的权最小边,distTo[v]即权值
 
 
public class PrimMST {private Edge[] edgeTo;//距树最近(最小权)边private double[] distTo;//最小权private boolean[] marked;//树中点private IndexMinPQ<Double> pq;//有效横切边public PrimMST(EdgeWeightedGraph G){edgeTo = new Edge[G.V()];distTo = new double[G.V()];marked = new boolean[G.V()];for (int v = 0; v < G.V(); v++)distTo[v] = Double.POSITIVE_INFINITY;//权值都初始为正无穷pq = new IndexMinPQ<>(G.V());distTo[0] = 0.0;//顶点0和0.0初始化起点pq.insert(0,0.0);while (!pq.isEmpty())visit(G,pq.delMin());}private void visit(EdgeWeightedGraph G, int v){marked[v] = true;for (Edge e:G.adj(v)){int w = e.other(v);if (marked[w]) continue;//忽略失效边v-wif(e.weight() < distTo[w]){//发现更短有效路径eedgeTo[w] = e;distTo[w] = e.weight();if (pq.contains(w)) pq.change(w, distTo[w]);//非首次加入树else pq.insert(w,distTo[w]);//首次加入树}}}public Iterable<Edge> edges(){Bag<Edge> mst = new Bag<>();for (int v = 1; v < edgeTo.length; v++)mst.add(edgeTo[v]);return mst;}}
算法分析
- 命题:Prim算法计算连通加权无向图G(V,E)的最小生成树所需空间和V成正比,时间和ElogV(最坏)成正比
 
证明:pq中顶点数最多为V,使用三个索引数组,则空间上限和V成正比.已知基于堆的索引优先队列的操作增长数量级是logV,则相加总时间和ElogV成正比
图示
