1. 什么是最小生成树(Minimum Spanning Trees)

  对于一个无向图,如果它的所有边都带有一定的数值(即带权),则会变成下面的样子

  

  假设这些点都是城市,每个城市之间的连线代表城市间的道路,线上的数字代表着道路的长短。当然,修越长的道路就需要越多的资源。

  那么如果要用最少的资源把所有城市都联系起来(即任意城市A能沿着道路抵达任意城市B),我们应该怎样建设道路呢?答案如下图:

  

  这就是最小生成树:用最小的权值总和(即数值总和)把所有点都联系起来。应注意到:最小生成树的边总数=此无向图的点总数-1。

  注意,最小生成树里是不应该有闭合循环的,如:

  

  权值为24的那条边显然是多余的。

2. 生成带权的无向图

  因为这种无向图只比普通无向图多了些权值,我们只需在每条边上多加一个变量来记录权值即可。

  使用的邻接列表也需要把权值信息写上,如下图:

  

  目前有两种比较主流的算法来找最小生成树:kruskal's algorithm(克鲁斯卡尔算法)和Prim's algorithm(普林演算法)。(中文译名采用音译的方法。)

  接下来,我们将逐一介绍这两种算法。

 

3. kruskal's algorithm(克鲁斯卡尔算法)

  从例子入手:

  

  为了容易理解,我们把所有边按权值大小排成递增的数组。实际代码操作时,只需要写个方法,让数组输出一个最小值即可。

  

  我们需要创建几个数组:

  创建一个点的数组Points,把最小生成树T的点存储起来;

  创建一个边的数组mst(Minimum spanning tree的简写)来把最小生成树的边都存储起来。

  创建一个边的数组pq把无向图里所有的边都存储起来。

  首先数组pq输出并移除一个最小值:0-7  0.16。

  由于目前的最小生成树T还没有点,所以把0和7加进Points。

  把0-7这条边加进mst。

  

  然后数组pq输出并移除一个最小值:2-3  0.17。

  检查2和3是否在Points中。如果不在,则把这两个点加入到Points中。

  把2-3这条边加进mst。

  

  然后数组pq输出并移除一个最小值:1-7  0.19。

  7已经在Points中了,只需把1加进Points里。

  把1-7这条边加进mst。

  

  然后数组pq输出并移除一个最小值:0-2  0.26。

  由于0,2都已经在Points里了,我们需要检查如果把0-2这条边加进最小生成树里是否会形成闭合循环。

  检查方法稍后介绍。

  如果不会形成闭合循环,则把这条边加进最小生成树T中;否则,则无视这条边,继续看下一条边。

  在这里,不形成闭合循环,把0-2这条边加进mst中。

  

  然后数组pq输出并移除一个最小值:5-7  0.28。

  7已经在Points中了,只需把5加进Points里。

  把5-7这条边加进mst。

  

  

  然后数组pq输出并移除一个最小值:1-3  0.29。

  由于1,3都已经在Points里了,我们需要检查如果把1-3这条边加进最小生成树里是否会形成闭合循环。

  会形成闭合循环,无视之。

  然后数组pq输出并移除一个最小值:1-5  0.32。

  由于1,5都已经在Points里了,我们需要检查如果把1-5这条边加进最小生成树里是否会形成闭合循环。

  会形成闭合循环,无视之。

  然后数组pq输出并移除一个最小值:2-7  0.34。

  2,7都已经在Points里,且会形成闭合循环,无视之。

  然后数组pq输出并移除一个最小值:4-5  0.35。

  5已经在Points中了,只需把4加进Points里。

  把5-4这条边加进mst。

  

  然后数组pq输出并移除一个最小值:2-1  0.36。

  2,1都已经在Points里,且会形成闭合循环,无视之。

  然后数组pq输出并移除一个最小值:4-7  0.37。

  4,7都已经在Points里,且会形成闭合循环,无视之。

  然后数组pq输出并移除一个最小值:4-0  0.38。

  4,0都已经在Points里,且会形成闭合循环,无视之。

  然后数组pq输出并移除一个最小值:6-2  0.4。

  2已经在Points中了,只需把6加进Points里。

  把6-2这条边加进mst。

  

  此时,mst里的边数=无向图的点总数-1。说明最小生成树已经形成了,算法结束。

  现在讨论如何检测新加入的边(v-w)是否会使最小生成树形成闭合循环。假设现在最小生成树的点总数为V。

  可以用深度优先搜索来检测v是否能抵达w,如果可以,说明最小生成树中已经有路连接v和w了,再加一条v-w会形成闭合循环。

  但是,还有一种方法更为高效:并查集算法。简单总结一下就是:

  v与和v相连的所有点形成一个区域,此区域用一个点a来代表。

  w与和w相连的所有点形成一个区域,此区域用一个点b来代表。

  然后对比a是否等于b,如果是,则说明v,w处于同一区域,最小生成树中已经有路连接v和w了,再加一条v-w会形成闭合循环;如果不是,说明v和w处于不同区域,可以加v-w进最小生成树。

  总结一下kruskal's algorithm(克鲁斯卡尔算法)的通用思路就是:

  把所有边放进一个数组里。

  然后数组输出并移除一个拥有最小权值的边,如果这条边加入到最小生成树内不会形成闭合循环,则把它加进去;否则无视它。

  如此循环,直到最小生成树的边总数=无向图的点总数-1为止。

  顺带一提:输出数值的最小值的方法可以把此数组做出最小堆的结构,然后输出第一个元素即可。

代码大概是这样子的:

  

4. Prim's algorithm(普林演算法)

  此算法有两种实现版本:懒惰算法版(lazy implementation)和贪心算法版(eager implementation)。

  懒惰算法和贪心算法是两种思想,贪心算法也被称为过度热情算法。

  从一个例子中感受一下:

  假设a在他的房间里愉快地玩耍,突然他妈叫他收拾房间。此时a有两个选择:要么马上收拾,要么等会再收拾。

  如果选择马上收拾,a会马上打扫房间,甚至把走廊也扫了一遍。这就是贪心算法。

  如果选择等会再收拾,a会等到他妈要来检查的前一瞬间才开始收拾。这就是懒惰算法。  

  对于一个程序来说,贪心算法就是当程序收到一个指令时,它不但会完成指令,还会过度热情地多做点其它事情;

  懒惰算法就是程序收到一个指令时,它会暂时无视之,等到我们要用到那个指令生成的结果时,程序才会去做这个指令。

  接下来,逐一介绍这两个实现版本:

普林演算法之懒惰实现版本

  从例子入手:

  

  我们需要创建几个数组:

  创建一个点的数组Points,把最小生成树T的点存储起来;

  创建一个边的数组mst(Minimum spanning tree的简写)来把最小生成树的边都存储起来。

  创建一个边的数组pq。

  首先,随便找一个点开始:如0.

  把0加入到Points里,把含点0的所有边加入到pq里。(为了方便理解,这里的边按权值递增排进数组,实际操作只需从数组中输出最小值,不必排序)

  

  然后pq输出并移除最小一个最小值:0-7  0.16。

  0已经在Points中了,只需把7加进Points里。

  把含点7的除了边的另一个端顶点已经在Points里之外的所有边加入到pq里。

  

  然后pq输出并移除最小一个最小值:1-7  0.19。

  7已经在Points中了,只需把1加进Points里。

  把含点1的除了边的另一个端顶点已经在Points里之外的所有边加入到pq里。

  

  然后pq输出并移除最小一个最小值:0-2  0.26。

  0已经在Points中了,只需把6加进Points里。

  把含点2的除了边的另一个端顶点已经在Points里之外的所有边加入到pq里。

  

  然后pq输出并移除最小一个最小值:2-3  0.17。

  2已经在Points中了,只需把3加进Points里。

  把含点3的除了边的另一个端顶点已经在Points里之外的所有边加入到pq里。

  

  然后pq输出并移除最小一个最小值:1-3  0.29。

  3,1都已经在Points里,无视之。

  然后pq输出并移除最小一个最小值:7-5  0.28。

  7已经在Points中了,只需把5加进Points里。

  把含点5的除了边的另一个端顶点已经在Points里之外的所有边加入到pq里。

  

  然后pq输出并移除最小一个最小值:1-5  0.32。

  5,1都已经在Points里,无视之。

  然后pq输出并移除最小一个最小值:7-2  0.34。

  7,2都已经在Points里,无视之。

  然后pq输出并移除最小一个最小值:4-5  0.35。

  5已经在Points中了,只需把4加进Points里。

  把含点4的除了边的另一个端顶点已经在Points里之外的所有边加入到pq里。

  

  然后pq输出并移除最小一个最小值:1-2  0.36。

  2,1都已经在Points里,无视之。

  然后pq输出并移除最小一个最小值:7-4  0.37。

  7,4都已经在Points里,无视之。

  然后pq输出并移除最小一个最小值:0-4  0.38。

  0,4都已经在Points里,无视之。

  然后pq输出并移除最小一个最小值:2-6  0.4。

  2已经在Points中了,只需把6加进Points里。

  把含点6的除了边的另一个端顶点已经在Points里之外的所有边加入到pq里。(没有可加的边)

  

  此时,mst里的边数=无向图的点总数-1。说明最小生成树已经形成了,算法结束。

  这个算法懒惰在哪呢?我们沿用那个收拾房间的例子。

  总结一下通用思路:

  1. 首先随便选一个点加进Points

  2. 然后把含此点的除了边的另一个端顶点已经在Points里之外的所有边加入到pq里。(孩子a听到要收拾房间,就把东西全部塞到房间里了)

  3. 然后pq输出并移除最小一个拥有最小值权值的边,如果此边的另一端点在Points里,则无视之;如果不在,则把此点加进Points里。(a的妈妈要来查房了,马上收拾。)

  4. 重复2,3步直到mst里的边数=无向图的点总数-1。

代码实现:

  

  

  

普林演算法之贪心实现版本

  从例子入手:

  

  我们需要创建几个数组:

  创建一个点的数组Points,把最小生成树T的点存储起来;

  创建一个边的数组mst(Minimum spanning tree的简写)来把最小生成树的边都存储起来。

  创建一个的数组pq。

  首先,随便找一个点开始:如0.

  把0加入到Points里,把点0能直接去的点加入到pq里。(为了方便理解,这里的边按权值递增排进数组,实际操作只需从数组中输出最小值,不必排序)

  

  然后数组输出并移除最小值:7. 把7对应的边7-0加入到mst里,把7加入Points里。

  点7能直接去的、不在Points里的点(1,2,5,4)加入到pq,但是2,4已经在pq里了,7-2的权值0.34比pq里面的0-2的权值大,故无视之;7-4的权值0.34比pq里面的0-4的权值大,故无视之。

  

  然后数组输出并移除最小值:1. 把1对应的边7-1加入到mst里,把1加入Points里。

  点1能直接去的、不在Points里的点(3,2,5)加入到pq,但是2,5已经在pq里了,1-2的权值0.36比pq里面的0-2的权值大,故无视之;1-5的权值0.32比pq里面的7-5的权值大,故无视之。

  

  然后数组输出并移除最小值:2. 把2对应的边0-2加入到mst里,把2加入Points里。

  点2能直接去的、不在Points里的点(3,6)加入到pq,但是3,6已经在pq里了,3-2的权值0.17比pq里面的1-3的权值小,故取代之;6-2的权值0.4比pq里面的0-6的权值小,故取代之。

  

  然后数组输出并移除最小值:3. 把3对应的边2-3加入到mst里,把3加入Points里。

  点3能直接去的、不在Points里的点(6)加入到pq,但是6已经在pq里了,3-6的权值0.52比pq里面的6-2的权值大,故无视之。

  

  然后数组输出并移除最小值:5. 把5对应的边7-5加入到mst里,把5加入Points里。

  点5能直接去的、不在Points里的点(4)加入到pq,但是4已经在pq里了,5-4的权值0.35比pq里面的0-4的权值小,故取代之。

  

  然后数组输出并移除最小值:4. 把4对应的边5-4加入到mst里,把4加入Points里。

  点4能直接去的、不在Points里的点(6)加入到pq,但是6已经在pq里了,4-6的权值0.93比pq里面的6-2的权值大,故无视之。

  

  然后数组输出并移除最小值:6. 把6对应的边6-2加入到mst里,把6加入Points里。

  点6没有能直接去的、不在Points里的点。最小生成树生成完毕,结束算法。

  

  

  总结一下通用思路:

  1.  首先随便选一个点加进Points。

  2.  然后把这个点能直接去的、不在Points里的点加入到数组pq中,把对应的那些边记录下来。如果要加的点已经存在于pq中,则看新加入的对应的边权值是否比已存在的小,如果是则取代之;否则无视之。

  3.  数组输出并移除拥有最小值的点,把这个点加进Points,这个点对应的边加进mst里。

  4.  重复2-3步,直到pq没有元素为止。

  这个算法贪心在哪?

  与懒惰版对比一下就很显然了: 第二步,懒惰版是直接把所有边塞进数组里,而贪心版是全部逐一比较(a会马上打扫房间,甚至把走廊也扫了一遍)。

转载于:https://www.cnblogs.com/mcomco/p/10334536.html

图表算法—最小生成树相关推荐

  1. JAVA:实现Graphs图表算法(附完整源码)

    JAVA:实现Graphs图表算法 package com.thealgorithms.datastructures.graphs;import java.util.ArrayList;class A ...

  2. [算法]最小生成树-普利姆算法

    2019独角兽企业重金招聘Python工程师标准>>> 目前正在看<大话数据结构>,其中介绍了普利姆算法,自己对算法理解能力太差,能够手写求出最小生成树,但是写出算法代码 ...

  3. 对应生成树的基本回路_数据结构与算法——最小生成树

    1 引言 在之前的文章中已经详细介绍了图的一些基础操作.而在实际生活中的许多问题都是通过转化为图的这类数据结构来求解的,这就涉及到了许多图的算法研究. 例如:在 n 个城市之间铺设光缆,以保证这 n ...

  4. Kruskal算法 最小生成树

    Kruskal算法 Kruskal的由来 Prim算法利用了MST的性质:假设N= (V,E)是一个连通图,U是顶点集V的一个非空子集,若(u,v)是一条最小权值的边,其中u属于U,v属于V-U,则必 ...

  5. prim算法 最小生成树

    Prim算法 Prim算法的由来 Prim算法利用了MST的性质:假设N= (V,E)是一个连通图,U是顶点集V的一个非空子集,若(u,v)是一条最小权值的边,其中u属于U,v属于V-U,则必存在一颗 ...

  6. prim算法 + 最小生成树的打印 C语言

    问题 要在n个城市之间假设网络,要求只架设n-1条线路,并保证个城市之间网络链接,已知城市间架设网络的统计数据,其中列出了有可能架设的网络和对应的成本求最低价格方案,以图形方式给出架设方案,并输出求解 ...

  7. [算法] 克鲁卡尔算法 最小生成树

    package com.guigu.algorithm.kruskal;import jdk.internal.org.objectweb.asm.tree.MultiANewArrayInsnNod ...

  8. Prim算法最小生成树Java超详解

    算法简介 算法流程 具体实现 Code Input Output 使用优先队列实现 API说明 Code Input Output 算法简介 Prim算法计算最小生成树,它的每一步都会为一颗生长树添加 ...

  9. 图论总结(欧拉路+Floyd所有结点最短+Bellman-Ford算法+SPFA+Dijsktra算法+Tarjan算法+最小生成树(prim+kruskal) )

    目录 欧拉路 判断欧拉路是否存在: ​​​​​​​ 最短路: Floyd算法 : ​​​​​​​​​​​​​​ Bellman-Ford: ​​​​​​​ Dijkstra 单源最短路 先附上例题: ...

最新文章

  1. 如让自己想学不好shell编程都困难?
  2. SQL Relay 0.47 发布,SQL 中间层
  3. 【操作系统】考研の内存管理方法(看不懂你来打我~!)
  4. 用Qt开发游戏时一些问题的解决方法(一)
  5. jvm性能调优 - 11J线上VM调优案例分享
  6. 深入浅出设计模式——组合模式(Composite Pattern)
  7. Linux第五章自测习题——Linux系列学习笔记
  8. 大学物理实验习题+答案/缓慢更新
  9. vs2012安装出错解决方案
  10. matlab qam调制函数,matlab实现16QAM调制解调
  11. Winhex的使用(慢慢更
  12. 仿网易LOFTER视差滚动列表
  13. 动态创建多个echarts图表
  14. 项目经理常用的12大工具及5大最佳项目管理软件
  15. jstat命令查看jvm的GC情况
  16. 使用python中PIL库进行切图时候出现了图片全黑
  17. Java封装继承多态练习题
  18. 传统分布式架构部署(apache+tomcat集群)
  19. 优信拍集团php面试题_【优信拍PHP面试】一次不错的面试体验,面试官根据实际出发提问。-看准网...
  20. python eval函数的神奇魔法

热门文章

  1. Windows 10和Linux Mint 18 双系统安装心得
  2. JAVA微信公众号开发第8篇JSSDK图片上传预览
  3. c语言11位电话号码,电话号码及日期时间提取(正则表达式 C语言)
  4. 6-2 实数 - 8. 交换实数
  5. 2022源支付V5.1.7完整源码+前/后端协议2.2
  6. Gouraud Shading(高洛德着色/高氏着色)
  7. 计算机中定点数和浮点数表示
  8. Immersion 签署通过 IC 合作伙伴 Dongwoon Anatech 提供触感技术的协议
  9. 让运维工程师不再蓝瘦、香菇
  10. 师徒制 带新员工 比较有效