本次要解决的问题是:你们村里那些坑坑洼洼的路,到底哪些路才是主干道?
小明:肯定是哪里都能到得了,并且去哪里都相对比较近,并且被大家共用程度高的路是啊!
具体是哪几条路呢?今天就可以给出准确答案
最小生成树的特点
1。可以到达图中任何一个顶点
2. 是一颗树(无环)
3. 最小生成树的边的权重之和是可以链接图中所有顶点的边的集合中,权值之和最小的(运用了贪婪算法思想)
4. 边数 = 图的顶点数量-1
先看主要代码,再看库代码
//Prim算法的 最小生成树 //时间 ElogE E为遍历原图中每条边,logE为优先队列(二叉堆)找到最小权重边的平均成本 //空间 V-1条Edge + V个顶点 public class LazyPrimMST implements MST { EdgeWeightedGraph orgEwg; //原始加权图 EdgeWeightedGraph singleEwg; //只有最小生成树的加权图 List<Edge> edges; //最小生成树的边 boolean[] marked; //顶点的访问 float weightSum; public LazyPrimMST(EdgeWeightedGraph ewg) { this.orgEwg = ewg; edges = new LinkedList<>(); marked = new boolean[ewg.v]; weightSum = 0; //只考虑一个连通图的情况 //改进:排除有多个子图的情况 //假设 ewg 是连通的 BinHeap2<Edge> pqedges = new BinHeap2<Edge>(); //最小生成树的性质:边数 = 图的顶点数量-1 visit2(pqedges, 0); while (!pqedges.isEmpty()) { Edge e = pqedges.pop(); int v = e.either(); int w = e.other(v); if (marked[v] && marked[w]) //已失效的横切边不处理 continue; edges.add(e); //将权重最小的加入到MST中 if (!marked[v]) visit2(pqedges, v); if (!marked[w]) visit2(pqedges, w); } //将找到的最小生成树转换为一个 EWG对象 singleEwg = new EdgeWeightedGraph(orgEwg.v()); for (Edge e : edges) { singleEwg.addEdge(e); } this.orgEwg = null; } private void visit2(BinHeap2<Edge> pqedges, int v) { marked[v] = true; //将访问顶点加入MST中 for (Edge e : orgEwg.adj(v)) if (!marked[e.other(v)]) pqedges.add(e); } //1.将关注顶点 周围的边加入 横切边集合 //2.找到横切边集合中权重最小的边 //3.将改边的对面顶点作为下一个关注顶点返回 private int visit(BinHeap2<Edge> pqedges, int v) { marked[v] = true; //将当前关注点加入最小生成树 for (Edge e : orgEwg.adj(v)) { //加入关注顶点的边到优先队列, 横切边集合 if (!marked[e.other(v)]) //只加入未失效的横切边 pqedges.add(e); } Edge tmpe = null; while (tmpe == null && !pqedges.isEmpty()) { tmpe = pqedges.pop(); if (marked[tmpe.either()] && marked[tmpe.other(tmpe.either())]) tmpe = null; //失效的横切边 } if (tmpe == null) //没有足够的边 return -1; edges.add(tmpe); //将最小权重的边加入到最小生成树 if (!marked[tmpe.either()]) //从最小权重的边里,找到未探索的对面顶点作为新的关注点 v = tmpe.either(); else v = tmpe.other(tmpe.either()); return v; } @Override public Iterable<Edge> edges() { return edges; } @Override public float weight() { if (weightSum == 0) { for (Edge e : edges) { weightSum += e.weight(); } } return weightSum; } public EdgeWeightedGraph getSingleEWGraph() { //只保留最小生成树的加权有向图 return singleEwg; } public static void main(String[] args) { // 村口 二狗子家 // 0--------------1 // |\ /| // | \ 你家 / | // | -----2---- | // | | // +---------3----+ // 希望小学 EdgeWeightedGraph ewg = new EdgeWeightedGraph(4); ewg.addEdge(0, 1, 2,"二麻二麻路"); ewg.addEdge(0, 2, 3,"挨打巷西段"); ewg.addEdge(0, 3, 4,"挨打巷东段"); ewg.addEdge(1, 2, 3.5f,"恶犬巷"); ewg.addEdge(1, 3, 2.5f,"希望之路"); System.out.println(ewg); System.out.println("======="); LazyPrimMST lp = new LazyPrimMST(ewg); System.out.println("最小生成树权重总和(村里主干道总长度): " + lp.weight()); for (Edge e : lp.edges()) { System.out.println(e.either() + "和" + e.other(e.either()) + "之间的路["+e.name+"], 路长:" + e.weight()); } } }
输出
最小生成树权重总和(村里主干道总长度): 7.5 0和1之间的路[二麻二麻路], 路长:2.0 1和3之间的路[希望之路], 路长:2.5 0和2之间的路[挨打巷西段], 路长:3.0
库代码:
//加权无向图 public class EdgeWeightedGraph { LinkedList<Edge>[] edges; //边的集合 final int v; //顶点数量 int e; //边的数量 public EdgeWeightedGraph(int v) { this.v = v; this.e = 0; edges = new LinkedList[v]; for (int i = 0; i < v; i++) edges[i] = new LinkedList<>(); } public void addEdge(int v, int w, float weight) { addEdge(new Edge(v, w, weight)); } public void addEdge(Edge e) { //添加一条边,在无向图中等于向2边顶点添加边(互相连通) edges[e.v].add(e); edges[e.w].add(e); this.e++; } public Iterable<Edge> adj(int v) { return edges[v]; } //返回所有边的集合 public Iterable<Edge> edges() { List<Edge> es = new LinkedList<>(); for (int v = 0; v < edges.length; v++) { for (Edge e : adj(v)) { if (e.other(v) > v) //加入顺序: 顶点序号逆序 es.add(e); } } return es; } public int v() {//顶点数 return v; } public int e() {//边数 return e; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int v = 0; v < v(); v++) { sb.append(v); sb.append(": "); for (Edge e : adj(v)) { sb.append(e.toString()); sb.append(", "); } sb.append('\n'); } return sb.toString(); } public static void main(String[] args) { EdgeWeightedGraph ewg = new EdgeWeightedGraph(4); ewg.addEdge(0, 1, 1); ewg.addEdge(0, 2, 2); ewg.addEdge(0, 3, 3); ewg.addEdge(1, 2, 3); System.out.println(ewg); } }
边对象
public class Edge extends Vertex implements Comparable<Edge> { public int w; public float weight; public String name; //from - to public Edge(int v, int w, float weight) { super(v); this.w = w; this.weight = weight; } public Edge(int v, int w, float weight, String name) { super(v); this.w = w; this.weight = weight; this.name = name; } public float weight() { return weight; } public int either() { return v; } public int other(int vertex) { if (vertex == this.v) return w; else if (vertex == this.w) return v; throw new RuntimeException("no such vertex " + vertex + " ,[v:" + v + " w:" + w + "]"); } @Override public int compareTo(@NonNull Edge another) { if (weight > another.weight) return 1; else if (weight < another.weight) return -1; return 0; } @Override public String toString() { return String.format("%d-%d %.2f", v, w, weight); } }
下期有Prim实时版,时间复杂度从 ElogE 变为 ElogV