求的带权图最小生成树的Prim算法和Kruskal算法

最小生成树的概念

最小生成树其实是最小权重生成树的简称。

一个连通图可能有多个生成树。当图中的边具有权值时,总会有一个生成树的边的权值之和小于或者等于其它生成树的边的权值之和。广义上而言,对于非连通无向图来说,它的每一连通分量同样有最小生成树,它们的并被称为最小生成森林。

以有线电视电缆的架设为例,若只能沿着街道布线,则以街道为边,而路口为顶点,其中必然有一最小生成树能使布线成本最低。

相关性质

存在个数

这张图表明一个图中可能有多个最小生成树
最小生成树在一些情况下可能会有多个。例如,当图的每一条边的权值都相同时,该图的所有生成树都是最小生成树。

如果图的每一条边的权值都互不相同,那么最小生成树将只有一个。这一定理同样适用于最小生成森林。

边的权值之和最低的子图

如果图的边的权值都为正数,那么最小生成树就是该图的所有包含所有顶点的子图中权值最低的子图。

环定理

对于连通图中的任意一个环:如果C中有边 e的权值大于该环中任意一个其它的边的权值,那么这个边不会是最小生成树中的边

切分定理

在一幅连通加权无向图中,给定任意的切分,它的横切边中权值最小的边必然属于图的最小生成树。

最小权值边

如果图的具有最小权值的边只有一条,那么这条边包含在任意一个最小生成树中。

相关算法

Prim算法

Prim算法的每一步都会为一棵生长中的树添加一条边,该树最开始只有一个顶点,然后会添加 V-1个边。每次总是添加生长中的树和树中除该生长的树以外的部分形成的切分的具有最小权值的横切边。Prim算法使用的就是切分定理思想
使用堆的LazyPrim算法的时间复杂度为O(ElogE)O(ElogE)O(ElogE)
使用索引堆优化过的Prim算法的时间复杂度为O(ElogV)O(ElogV)O(ElogV)

Kruskal算法

按照边的权重顺序(从小到大)将边加入生成树中,但是若加入该边会与生成树形成环则不加入该边。直到树中含有 V-1条边为止。这些边组成的就是该图的最小生成树。
Kruskal算法使用的是环定理思想
Kruskal算法的时间复杂度为ElogEElogEElogE

代码实现

LazyPrim
package MinimumSpanTrees;import java.util.Vector;/*** @ Description: LazyPrim算法求最小生成树* @ Date: Created in 08:38 2018/8/2* @ Author: Anthony_Duan*/
public class LazyPrimMST<Weight extends Number & Comparable> {private WeightedGraph<Weight> G;private MinHeap<Edge<Weight>> pq;private boolean[] marked;private Vector<Edge<Weight>> mst;private Number mstWeight;public LazyPrimMST(WeightedGraph<Weight> graph) {//初始化算法G = graph;//开辟一个空间为图的边数的最小堆pq = new MinHeap<Edge<Weight>>(G.E());//开辟一个数组用来标记是否已经访问过marked = new boolean[G.V()];//保存最小生成树的边mst = new Vector<Edge<Weight>>();//首先从0索引开始访问visit(0);//如果堆不为空 也就是堆中仍有边while (!pq.isEmpty()) {//使用最小堆找出已经访问的边中权值最小的边Edge<Weight> e = pq.extractMin();//如果这个边的两个端点都已经访问过了,则这个边不符合条件if (marked[e.v()] == marked[e.w()]) {continue;}//如果有一个端点没有访问就把该边添加到mst中mst.add(e);//继续访问这条边没有被访问的那个节点if (!marked[e.v()]) {visit(e.v());} else {visit(e.w());}}/*** 计算最小生成树的权值* 这里没有  这么写*      mstWeight = 0;*         for (int i = 0; i < mst.size(); i++) {*             mstWeight =mstWeight.doubleValue()+mst.elementAt(i).wt().doubleValue();*         }**  是因为我们的mstWeight这个变量不一定是整型(或者浮点型)。*  而是Weight类型。Weight有可能是用户自定义的类型。*  比如Weight是用户自己定义的大整数类型;高精度浮点数类性;*  或者复数类型;向量;等等。此时,直接赋值为0是不对的,*  需要调用用户自定义的无参数构造函数,得到这个自定义类型的“零”。*/mstWeight = mst.elementAt(0).wt();for (int i = 1; i <mst.size() ; i++) {mstWeight = mstWeight.doubleValue() + mst.elementAt(i).wt().doubleValue();}}/**** @param v*/private void visit(int v) {assert !marked[v];//将节点标记为访问过marked[v] = true;//将和节点v相连接的所有未访问的边放入最小堆中for (Edge<Weight> e :G.adj(v)) {if (!marked[e.other(v)]) {pq.insert(e);}}}Vector<Edge<Weight>> mstEdges() {return mst;}Number result() {return mstWeight;}// 测试 Lazy Primpublic static void main(String[] args) {String filename = "/Users/duanjiaxing/IdeaProjects/Algorithm/src/MinimumSpanTrees/testG1.txt";int V = 8;SparseWeightedGraph<Double> g = new SparseWeightedGraph<Double>(V, false);ReadWeightedGraph readGraph = new ReadWeightedGraph(g, filename);// Test Lazy Prim MSTSystem.out.println("Test Lazy Prim MST:");LazyPrimMST<Double> lazyPrimMST = new LazyPrimMST<Double>(g);Vector<Edge<Double>> mst = lazyPrimMST.mstEdges();for( int i = 0 ; i < mst.size() ; i ++ ) {System.out.println(mst.elementAt(i));}System.out.println("The MST weight is: " + lazyPrimMST.result());System.out.println();}
}
Prim
package MinimumSpanTrees;import java.util.Vector;/*** @ Description:使用优化的Prim算法求图的最小生成树* @ Date: Created in 09:46 2018/8/2* @ Author: Anthony_Duan*/
public class PrimMST<Weight extends Number & Comparable> {//图的引用private WeightedGraph G;//最小索引堆,算法辅助数据结构private IndexMinHeap<Weight> ipq;//访问的点所对应的边,算法辅助数据结构private Edge<Weight>[] edgeTo;//标记数组,在算法运行过程中标记节点i是否被访问private boolean[] marked;//最小生成树所包含的所有边private Vector<Edge<Weight>> mst;//最小生成树的权值private Number mstWeight;//构造函数,使用Prim算法囚徒的最小生成树public PrimMST(WeightedGraph graph) {G = graph;assert (graph.E() >= 1);ipq = new IndexMinHeap<Weight>(graph.V());//算法初始化marked = new boolean[G.V()];edgeTo = new Edge[G.V()];for (int i = 0; i < G.V(); i++) {marked[i] = false;edgeTo[i] = null;}mst = new Vector<Edge<Weight>>();//Primvisit(0);while (!ipq.isEmpty()) {//使用最小索引堆找出已经访问的边中权值最小的边//最小索引堆中存储的是点的索引,通过点的索引找到相应的边int v = ipq.extractMinIndex();assert (edgeTo[v] != null);mst.add(edgeTo[v]);visit(v);}//计算最小生成树的权值mstWeight = mst.elementAt(0).wt();for (int i = 1; i < mst.size(); i++) {mstWeight = mstWeight.doubleValue()+mst.elementAt(i).wt().doubleValue();}}//访问节点vvoid visit(int v) {assert !marked[v];marked[v] = true;//将和节点v相连的未访问的另一端点,和与之相连接的边,放入最小堆中for (Object item : G.adj(v)) {Edge<Weight> e = (Edge<Weight>) item;int w = e.other(v);//如果边的另一端点未被访问if (!marked[w]) {//如果从没有考虑过这个端点,直接将这个端点和与之对应的边加入索引堆if (edgeTo[w] == null) {edgeTo[w] = e;ipq.insert(w, e.wt());}//如果曾经考虑过这个端点,但现在的边比之前考虑的边更短,则进行替换else if (e.wt().compareTo(edgeTo[w].wt()) < 0) {edgeTo[w] = e;ipq.change(w, e.wt());}}}}//返回最小生成树的所有边Vector<Edge<Weight>> mstEdges() {return mst;}//返回最小生成树的权值Number result() {return mstWeight;}// 测试 Primpublic static void main(String[] args) {String filename = "/Users/duanjiaxing/IdeaProjects/Algorithm/src/MinimumSpanTrees/testG1.txt";int V = 8;SparseWeightedGraph<Double> g = new SparseWeightedGraph<Double>(V, false);ReadWeightedGraph readGraph = new ReadWeightedGraph(g, filename);// Test Prim MSTSystem.out.println("Test Prim MST:");PrimMST<Double> primMST = new PrimMST<Double>(g);Vector<Edge<Double>> mst = primMST.mstEdges();for( int i = 0 ; i < mst.size() ; i ++ ) {System.out.println(mst.elementAt(i));}System.out.println("The MST weight is: " + primMST.result());System.out.println();}
}
KruskalMST
package MinimumSpanTrees;import java.util.Vector;/*** @ Description:Kruskal方法求最小生成树* @ Date: Created in 11:04 2018/8/2* @ Author: Anthony_Duan*/
public class KruskalMST <Weight extends Number & Comparable>{//最小生成树所包含的所有的边private Vector<Edge<Weight>> mst;//最小生成树的权值private Number mstWeight;//构造函数,使用Kruskal算法graph的最小生成树public KruskalMST(WeightedGraph graph) {mst = new Vector<Edge<Weight>>();//将图中的所有边存放到一个最小堆中MinHeap<Edge<Weight>> pq = new MinHeap<Edge<Weight>>(graph.E());for (int i = 0; i < graph.V(); i++) {for (Object item :graph.adj(i)) {Edge<Weight> e = (Edge<Weight>) item;if (e.v() <= e.w()) {pq.insert(e);}}}//创建一个并查集,来查看已经访问的节点的联通情况UnionFind uf = new UnionFind(graph.V());while (!pq.isEmpty() && mst.size() < graph.V() - 1) {//从最小堆中一次从小到大取出所有的边Edge<Weight> e = pq.extractMin();//如果该边的两个端点是连通的,说明加入该边将产生环,所以扔掉这条边if (uf.isConnected(e.v(), e.w())) {continue;}//否则,将这条边添加到最小生成树,同时标记边的两个端点连通mst.add(e);uf.unionElements(e.v(), e.w());}//计算最小生成树的权值mstWeight = mst.elementAt(0).wt();for (int i = 1; i <mst.size() ; i++) {mstWeight = mstWeight.doubleValue()+mst.elementAt(i).wt().doubleValue();}}// 返回最小生成树的所有边Vector<Edge<Weight>> mstEdges(){return mst;}// 返回最小生成树的权值Number result(){return mstWeight;}// 测试 Kruskalpublic static void main(String[] args) {String filename = "/Users/duanjiaxing/IdeaProjects/Algorithm/src/MinimumSpanTrees/testG1.txt";int V = 8;SparseWeightedGraph<Double> g = new SparseWeightedGraph<Double>(V, false);ReadWeightedGraph readGraph = new ReadWeightedGraph(g, filename);// Test KruskalSystem.out.println("Test Kruskal:");KruskalMST<Double> kruskalMST = new KruskalMST<Double>(g);Vector<Edge<Double>> mst = kruskalMST.mstEdges();for( int i = 0 ; i < mst.size() ; i ++ ) {System.out.println(mst.elementAt(i));}System.out.println("The MST weight is: " + kruskalMST.result());System.out.println();}
}

求的带权图最小生成树的Prim算法和Kruskal算法相关推荐

  1. 加权无向图与最小生成树(Prim算法和Kruskal算法)

    目录 0 引入 1 图的最小生成树定义及相关约定 2 最小生成树原理 2.1 性质 2.2 切分定理 3 贪心思想 4 Prim算法 4.1 算法步骤 4.2 API设计 4.3 Java代码演示 5 ...

  2. 最小生成树之Prim算法和Kruskal算法

    一个连通图可能有多棵生成树,而最小生成树是一副连通加权无向图中一颗权值最小的生成树,它可以根据Prim算法和Kruskal算法得出,这两个算法分别从点和边的角度来解决. Prim算法 输入:一个加权连 ...

  3. 【最小生成树】Prim算法和Kruskal算法的区别对比

    Prim算法和Kruskal算法都是从连通图中找出最小生成树的经典算法- 从策略上来说,Prim算法是直接查找,多次寻找邻边的权重最小值,而Kruskal是需要先对权重排序后查找的- 所以说,Krus ...

  4. 最小生成树的prim算法和kruskal算法

    <span style="font-size:18px;">#include "stdafx.h" #include <iostream> ...

  5. 作业1-采用Prim算法和Kruskal算法构造最小生成树

    采用Prim算法和Kruskal算法构造最小生成树 实验报告 1.问题 2.解析 (1)Prim算法 (2)Kruskal算法 3.设计 (1)Prim算法 (2)Kruskal算法 4.分析 (1) ...

  6. 【Java数据结构与算法】第十九章 贪心算法、Prim算法和Kruskal算法

    第十九章 贪心算法.Prim算法和Kruskal算法 文章目录 第十九章 贪心算法.Prim算法和Kruskal算法 一.贪心算法 1.介绍 2.支付问题 二.Prim算法 1.最小生成树 2.介绍 ...

  7. matlab实现prim算法,Prim算法和Kruskal算法的Matlab实现

    Prim算法和Kruskal算法的Matlab实现 <计算机仿真>期末大作业 Prim算法和Kruskal算法的Matlab实现 05605刘禹050697(30) 连线问题应用举例: 欲 ...

  8. 【数据结构】最小生成树问题(Prim算法和Kruskal算法)

    相关概念 连通图与它的生成树 连通图的生成树是包含图中全部顶点的一个极小连通子图.若图的顶点数为n,则它的生成树含有n-1条边.一个连通图可能拥有多个生成树. 最小生成树(Minimum-Spanni ...

  9. Prim算法和Kruskal算法

       Prim算法和Kruskal算法都能从连通图找出最小生成树.区别在于Prim算法是以某个顶点出发挨个找,而Kruskal是先排序边,每次选出最短距离的边再找. 一.Prim(普里姆算法)算法: ...

最新文章

  1. 微服务场景下的数据一致性解决方案
  2. python里的体格是啥r_numpy.数组形状(R,1)和(R,)之间的差异
  3. fseek获取大于4G的文件大小的问题
  4. Google C++编程风格指南
  5. Serverless 解惑——函数计算如何访问 Mongo 数据库
  6. linux 上删除docker 虚悬镜像
  7. 10拨号拒绝远程连接_ADLS动态拨号vps常见的问题
  8. php把一个数组放在另一个数组的后面,在PHP中的另一个数组之间注入一个数组
  9. AD4003 VHDL设计及仿真
  10. java后端项目怎么实现图片预览_项目经验不重样!3个基于 SpringBoot 的图片识别处理系统送给你!...
  11. 2009 CCTV体坛风云人物颁奖盛典,精彩语录
  12. 360天擎默认卸载密码_用好360(四)
  13. keras报错ValueError: No data provided for XXX
  14. 利用计算机打字教学设计,第6课 争当打字小能手教学设计
  15. 你确定了项目,就不要乱变了
  16. 三星30pin引脚_USB3.0针脚定义、引脚定义(精校版本)
  17. java用switch判断日期_Java-用switch判断季节
  18. C语言switch语句无break
  19. RS雷达转Velodyne雷达数据Failed to find match for field ‘intensity‘
  20. 网易2018实习生招聘笔试题的收获

热门文章

  1. cmath库里的常用函数
  2. RFID叉车读写器在智能仓库收发货环节中的应用-铨顺宏
  3. PixiJS学习(6)文本
  4. 企业为什么使用企业邮箱?为什么用腾讯企业邮箱?
  5. Java考试试题 [框架阶段考试试卷] 笔试+机试(A卷)
  6. 软件测试之 购物车测试用例
  7. 红光光浴,对比艾灸、汗蒸、光波房、能量仓等
  8. 即将2023年了,我好想念那些2022年离职的兄弟
  9. 基因数据处理44之cloud-scale-bwamem安装
  10. 真假4K电视检测:一张图足矣