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

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

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

最小生成树的特点

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. HDU2833-WuKong(求不同起点,终点最短路的交点最多数量)

    Liyuan wanted to rewrite the famous book "Journey to the West" ("Xi You Ji" in C ...

  2. JavaEE基础(04):会话跟踪技术,Session和Cookie详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.会话跟踪 1.场景描述 比如登录某个购物网站,身份识别成功后,在网站下单,支付 等操作,这些操作中当前登录用户信息必须是共享的,这样这些操 ...

  3. iSensor APP 之 摄像头调试 MT9D001 MT9P031 测试小结 200万像素和500万像素摄像头

    iSensor APP 之 摄像头调试  MT9D001 MT9P031 测试小结 iSensor app 非常适合调试各种摄像头,已测试通过的sensor有: l  OV7670.OV7725.OV ...

  4. 【HTTP】267- HTTP 的15个常见知识点复习

    前言 自从入职新公司到现在,我们前端团队内部一直在做 ?每周一练 的知识复习计划,我之前整理了一个 [每周一练 之 数据结构与算法] (https://juejin.im/post/5ce2a20e6 ...

  5. eclipse右下角一直在loading jar文件,如何关闭?

    通常导入项目的时候,右下角会显示download一些jar包或者其它内容,速度非常慢,如果你长得很帅很美可能更拖网速,最后可能会超时报错. 大招来了!!!   首先,打开windows->pre ...

  6. django----cookie与session 和 中间件

    目录 cookie与session简介及操作 cookie django中操作cookie cookie超时时间 删除cookie session session操作 设置 key value发生了什 ...

  7. Python 浮点数的冷知识

    本周的PyCoder's Weekly 上分享了一篇小文章,它里面提到的冷知识很有意思,我稍作补充,分享给大家. 它提到的部分问题,读者们可以先思考下: 若两个元组相等,即 a==b 且 a is b ...

  8. Elasticsearch 监控指标解析

    1.集群监控 集群监控主要包括两个方面的内容,分别是集群健康情况和集群的运行状态. 集群健康状态可以通过以下api获取: http://ip:9200/_cluster/health?pretty 关 ...

  9. JS---案例:移动元素,封装动画函数

    案例:移动元素,封装动画函数 1. div要移动,要脱离文档流---position:absolute 2. 如果样式的代码是在style的标签中设置,外面是获取不到 3. 如果样式的代码是在styl ...

  10. HTTP (了解URL)

    HTTP-URL URL是统一资源定位符,是互联网上标准的资源地址表示方法 URL组成: 协议头 用户名:密码(FTP) 主机名(域名). 三级域名.二级域名.顶级域名 / [IP] 端口号 目录/文 ...