本次要解决的问题是:你们村里那些坑坑洼洼的路,到底哪些路才是主干道?

小明:肯定是哪里都能到得了,并且去哪里都相对比较近,并且被大家共用程度高的路是啊!

具体是哪几条路呢?今天就可以给出准确答案

最小生成树的特点

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

即时版

主要使用了带索引的优先队列IndexMinPQ,支持替换操作(change)

并且对已失效的边用不加入优先队列,以及替换操作减少对失效横切边在优先队列中的排序操作(将时间复杂度从 ElogE 降为 ElogV,因为优先队列里同时只存有最多V-1个边)

代码:

//即时prim算法 可对加权无向图生成最小生成树
//时间 ElogV
//空间 3V+(V-1)
//允许平行边,自环(不起作用?),负权重
public class PrimMst implements Mst {
EdgeWeightedGraph ewg;
boolean[] marked; //记录已经被访问的,加入到最小生成树中的顶点;标记为true说明其edges 和 weights 被纳入最小生成树
float[] weights; //从最小生成树到顶点v的最小权重边的权重值
IndexMinPQ<Float> impq; //小根索引二叉堆,记录最多V-1个,从最小生成树到目标顶点的边的权重;index:目标顶点索引,val:最小权重边的权重
Edge[] edges; //属于最小生成树中的边的集合,有V-1条
float weightSum = Float.POSITIVE_INFINITY;//最小生成树的边的权重总和 public PrimMst(EdgeWeightedGraph ewg) {
this.ewg = ewg;
marked = new boolean[ewg.v()];
weights = new float[ewg.v()];
edges = new Edge[ewg.v()]; //最小生成树中最多有 v-1 条边,为了方便直接用顶点号做下标索引号直接申请v个元素(0号下标的边不会被更新)
impq = new IndexMinPQ<>(ewg.v()); //每个顶点对应一条最小生成树到它的边
Arrays.fill(weights, Float.POSITIVE_INFINITY); visit(0, 0); //从0开始访问
while (!impq.isEmpty()) { //从优先队列中取出权重最小的边,以及从最小生成树通过该边所到达的顶点号
visit(impq.topIndex() - 1, impq.delTop());
}
this.ewg = null;
} //对顶点v进行访问,并给出从最小生成树到达该顶点的边的最小权重
private void visit(int v, float weight) {
marked[v] = true;
weights[v] = weight;
for (Edge e : ewg.adj(v)) {
int w = e.other(v);
if (marked[w] || weights[w] < e.weight()) //已经在mst中,则跳过 或 未在mst中,但存在另一条到达该点的权重更小的边 也跳过(contain)
continue;
if (impq.contain(w + 1)) //优先队列中存在到达该点的边,将权重更新为更小的
impq.change(w + 1, e.weight());
else
impq.insert(w + 1, e.weight()); //首次遇到该顶点,将到达该顶点的边的权重和顶点号加入优先队列
edges[w] = e;
weights[w] = e.weight();
}
} @Override
public Iterable<Edge> edges() {
List<Edge> list = new LinkedList<>();
for (int v = 1; v < edges.length; v++) {
list.add(edges[v]);
}
return list;
} @Override
public float weight() {
if (weightSum == Float.POSITIVE_INFINITY && edges.length > 0) {
weightSum = 0;
for (float f : weights) {
weightSum += f;
}
}
return weightSum;
} public static void main(String[] args) {
demo2();
} private static void demo2() {
// 村口 二狗子家
// 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("======="); PrimMst lp = new PrimMst(ewg);
System.out.println("最小生成树权重总和(村里主干道总长度): " + lp.weight());
for (Edge e : lp.edges()) {
System.out.println(e.either() + "和" + e.other(e.either()) + "之间的路[" + e.name + "], 路长:" + e.weight());
}
System.out.println("\n=======");
}
}

输出:

=======
最小生成树权重总和(村里主干道总长度): 7.5
0和1之间的路[二麻二麻路], 路长:2.0
0和2之间的路[挨打巷西段], 路长:3.0
1和3之间的路[希望之路], 路长:2.5
=======

库代码:

//加权无向图
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);
}
}

链接:索引优先队列

IndexMinPQ

加权无向图 最小生成树 Prim算法 延迟版和即时版 村里修路该先修哪的更多相关文章

  1. 最小生成树—prim算法

    最小生成树prim算法实现 所谓生成树,就是n个点之间连成n-1条边的图形.而最小生成树,就是权值(两点间直线的值)之和的最小值. 首先,要用二维数组记录点和权值.如上图所示无向图: int map[ ...

  2. Highways POJ-1751 最小生成树 Prim算法

    Highways POJ-1751 最小生成树 Prim算法 题意 有一个N个城市M条路的无向图,给你N个城市的坐标,然后现在该无向图已经有M条边了,问你还需要添加总长为多少的边能使得该无向图连通.输 ...

  3. 图论算法(五)最小生成树Prim算法

    最小生成树\(Prim\)算法 我们通常求最小生成树有两种常见的算法--\(Prim\)和\(Kruskal\)算法,今天先总结最小生成树概念和比较简单的\(Prim\)算法 Part 1:最小生成树 ...

  4. 数据结构代码整理(线性表,栈,队列,串,二叉树,图的建立和遍历stl,最小生成树prim算法)。。持续更新中。。。

    //归并排序递归方法实现 #include <iostream> #include <cstdio> using namespace std; #define maxn 100 ...

  5. 最小生成树Prim算法(邻接矩阵和邻接表)

    最小生成树,普利姆算法. 简述算法: 先初始化一棵只有一个顶点的树,以这一顶点开始,找到它的最小权值,将这条边上的令一个顶点添加到树中 再从这棵树中的所有顶点中找到一个最小权值(而且权值的另一顶点不属 ...

  6. SWUST OJ 1075 求最小生成树(Prim算法)

    求最小生成树(Prim算法) 我对提示代码做了简要分析,提示代码大致写了以下几个内容 给了几个基础的工具,邻接表记录图的一个的结构体,记录Prim算法中最近的边的结构体,记录目标边的结构体(始末点,值 ...

  7. 最小生成树,Prim算法与Kruskal算法,408方向,思路与实现分析

    最小生成树,Prim算法与Kruskal算法,408方向,思路与实现分析 最小生成树,老生常谈了,生活中也总会有各种各样的问题,在这里,我来带你一起分析一下这个算法的思路与实现的方式吧~~ 在考研中呢 ...

  8. 最小生成树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind

    最小支撑树树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind 最小支撑树树 前几节中介绍的算法都是针对无权图的,本节将介绍带权图的最小 ...

  9. 数据结构之最小生成树Prim算法

    普里姆算法介绍 普里姆(Prim)算法,是用来求加权连通图的最小生成树算法 基本思想:对于图G而言,V是所有顶点的集合:现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最 ...

随机推荐

  1. CoderForces999E-Reachability from the Capital

    E. Reachability from the Capital time limit per test 2 seconds memory limit per test 256 megabytes i ...

  2. Openfiler存储搭建

    说明: Openfiler是一个基于Linux的开源免费网络存储管理操作系统,通过WEB界面对存储磁盘进行管理,支持iSCSI.NFS 等网络存储协议. 目前最新版本:openfileresa-2.9 ...

  3. WPF 画一个3D矩形并旋转

    具体的代码还是线性代数. 主要是旋转和平移. 这个例子的中模型是在世界原点建立.所以旋转会以自身轴心旋转. 如果不在世界原点建立模型,还想以自身为旋转轴旋转. 则是需要以下步骤: 模型的中心点为V1( ...

  4. 学Maven,这篇万余字的教程,真的够用了!

    1 Maven 介绍 1.1 为什么使用 Maven 由于 Java 的生态非常丰富,无论你想实现什么功能,都能找到对应的工具类,这些工具类都是以 jar 包的形式出现的,例如 Spring,Spri ...

  5. Win32_PhysicalMedia 硬盘 参数说明

    Caption 物理内存还虚拟内存 Description 描述和Caption一样 InstallDate 安装日期(无值) Name 名字 Status 状态 CreationClassName ...

  6. Oracle 12C CDB、PDB常用管理命令

    Oracle 12C CDB.PDB常用管理命令 --查看PDB信息(在CDB模式下) show pdbs  --查看所有pdbselect name,open_mode from v$pdbs;  ...

  7. struct socket结构体详解

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://weiguozhihui.blog.51cto.com/3060615/15852 ...

  8. 来看下,C# WebService WSDL自动生成代码,数组参数的BUG。。。ArrayOfString

    ArrayOfString ArrayOfString ArrayOfString 解决C#客户端 ArrayOfString 参数问题.(希望搜索引擎能搜到,帮你解决神奇的ArrayOfString ...

  9. 你不知道的JavaScript(上)this和对象原型(四)原型

    五章 原型 1.[[ Prototype ]] JavaScript 中的对象有一个特殊的 [[Prototype]] 内置属性,其实就是对于其他对象的引用.几乎所有的对象在创建时 [[Prototy ...

  10. 由malloc和new引发的段错误

    class Queue{ private: struct node{ string data; struct node * next,*priv; } private: struct node * p ...