本次要解决的问题是:你们村里那些坑坑洼洼的路,到底哪些路才是主干道?
小明:肯定是哪里都能到得了,并且去哪里都相对比较近,并且被大家共用程度高的路是啊!
具体是哪几条路呢?今天就可以给出准确答案
最小生成树的特点
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