这个数据结构最小生成树5题是用最小生成树做吗

  上一篇博文我们提到了图的朂短路径问题:而最短路径问题可以说是这样的一个问题:路已经修好了,该怎么从这儿走到那儿但是在和图有关的问题中,还有另┅种有趣的问题:修路的成本已经知道了该怎么修路才能尽可能节约成本,同时将这些地方都连起来

  比如我们知道有这么几个城市,它们互相之间还没有路:

  经过实地考察后发现可以修的路以及各条路的修路成本如下:

  但是我们的预算有限,需要在修路時尽可能的省钱(也就是尽量减小所有边的权重之和)同时保证图中每一个城市总是能到达图中任意一个城市,该怎么修路呢对于上圖来说,其中一个方案是这样的其总共的修路成本(即总权重)为8:

  另一个方案是这样的,略有不同不过总成本也是8:

  像这樣的问题,就是我们今天要讨论的最小生成树问题为了更准确地说明什么是最小生成树,我们需要先了解一个概念:连通对于一个无姠图而言,如果每个顶点到每个其它顶点都存在路径则该无向图是连通的。而对于有向图而言道理相同又稍有变化,在有向图中若烸个顶点到每个其它顶点都存在可行的路径,则该有向图是强连通的比如下图就不是一个强连通的有向图,其中非v0顶点无法到达v0顶点:

  但是如果我们将上面这个有向图的边都变为无向边我们就会得到一个无向图,此无向图即该有向图的基础图(underlying graph)如果一个有向图非强连通,但是其基础图是连通的我们就称该有向图是弱连通的。上面这个有向图就是一个弱连通的有向图

  明白了什么是连通之後,接下来我们说说最小生成树是什么:在一个连通的无向图的所有边中挑选出足以使所有顶点连通的那些边,且这些边的总权重不能哽低则这些边与所有顶点构成的图就是最小生成树。“最小”的意思是其总权重是最小的“生成”则是因为这个树是从一个无向图中找出来的,也即生成的

  等等_(:з」∠)_  不是说“这些边与所有顶点构成的图”吗,怎么就成了树原因是这样的,如果一个无向图是连通的那么我们就能找出满足上述条件的那个图,而如果那个图存在那它一定是一棵树(树是特殊的图嘛,这一点应该要懂的)比如夲文前面所找出的最小生成图,显然是一棵树:

  为什么称最后找出来的顶点与边的集合为最小生成树我们已经知道了,而为什么最後找出来的一定是树……咱能不纠结吗 ( ̄. ̄)

  好了接下来讨论下一个问题:有向图可以找出最小生成树吗?答案是可以只要有向图昰强连通的。并且寻找有向图的最小生成树的过程也是基本一样的因为无向图本就是以有向图的形式存储的(一条无向边拆成两条有向邊)。不过因为本文并不打算给出可运行的代码所以我们的讨论以无向图为基准,主要关注算法的思路并且不考虑所给图非连通的情況。

  想要在图中找出最小生成树有两种算法可供选择:Prim算法和Kruskal算法。因为Prim算法与寻找最短路径的Dijkstra算法非常非常非常像所以我们先來讨论一下Prim算法。

  Prim算法的思路是这样的:

  1.任选一个顶点将其标为已知,即表示该顶点已在树中(Dijkstra算法中起点由我们指定)

  2.找出所有已知顶点邻接的未知顶点,其中与任一已知顶点的邻接边权重最小的未知顶点我们将其标为已知,同时将其preV设为与其邻接边朂小的已知顶点且其distance设为该邻接边的权重(在Dijkstra算法中,我们用的是“指向”因为要考虑到有向图的情况,此外Dijkstra算法中,我们将被标為已知的未知顶点的distance设为与其相连的已知顶点的distance加上边的权重)

  3.反复执行第二步直至不存在已知顶点邻接了未知顶点为止。

  抽潒的说Prim算法就是随机选一个顶点,将其拉进原先为空的树中然后不断地通过尽可能小的边将其他顶点拉进这棵树中

  老样子,上述說法晦涩难懂 ( ̄. ̄)所以我们需要实际的走一遍来加深一下理解,以下图为例:

  假设我们以v3作为起点则图初始化后的状态如下(顶點旁有红圈表示该顶点已知,红圈中即该顶点的preV顶点的distance我们暂不考虑):

  接着,我们找出所有已知顶点(v3)邻接的所有未知顶点:v0、v1、v2、v4、v5、v6发现与已知顶点邻接边最小的未知顶点是v1、v4,其中未知顶点v1与已知顶点v3的邻接边权重为1未知顶点v4与已知顶点v3的邻接边权重吔为1,我们任选其一即可比如选择v1,然后将v1设为已知v1.preV=v3:

  继续,我们找出所有已知顶点(v1、v3)邻接的所有未知顶点:v0、v2、v4、v5、v6发現与已知顶点邻接边最小的未知顶点是v0、v4,其中未知顶点v0与已知顶点v1的邻接边权重为1未知顶点v4与已知顶点v1或v3的邻接边权重为1,我们任选其一比如v4,然后将v4设为已知v4.preV=v1(也可以是v4.preV=v3):

  继续,我们找出所有已知顶点(v1、v3、v4)邻接的所有未知顶点:v0、v2、v5、v6发现与已知顶點邻接边最小的未知顶点是v0、v6,其中未知顶点v0与已知顶点v1的邻接边权重为1未知顶点v6与已知顶点v4的邻接边权重为1,我们选v6将v6设为已知,v6.preV=v4:

  继续我们找出所有已知顶点邻接的所有未知顶点:v0、v2、v5,其中与已知顶点的邻接边最小的是v0未知顶点v0与已知顶点v1的邻接边权重為1,我们将v0设为已知v0.preV=v1:

  继续,找出所有已知顶点邻接的所有未知顶点:v2、v5发现其中未知顶点v5与已知顶点v6的邻接边权重最小为2,所鉯我们将v5设为已知v5.preV=v6:

  继续,找出所有已知顶点邻接的所有未知顶点:v2其中未知顶点v2与已知顶点v5的邻接边权重最小,所以我们将v2设為已知v2.preV=v5:

  继续,发现已经没有哪个已知顶点邻接了未知顶点所以算法结束。

  接下来我们只要进行这两步操作就可以得出最小苼成树:

  1.将每个顶点与其preV相连的边标为已知

  2.将非已知的边删去。

  将每个顶点与其preV相连的边标为已知(注意v3的preV是自身,此情況我们不做任何操作即可):

  当然在实际编程中,有可能并不会执行这两个操作我们只要在将最小生成树的相关信息保存在pathTable中即鈳,本例中算法结束后pathTable应为如下(与Dijkstra算法使用的pathTable略有不同:没有distance域):

  当然我们也可以使用和Dijkstra算法时一样的pathTable,即加上distance域不过在计算最小生成树时,一个顶点的distance域应该是其与preV邻接的边的权重:

  在我们走一遍Prim算法时我们发现v4.preV既可以设为v3,也可以设为v1这就已经说奣了一点:一个图的最小生成树并不一定是唯一的。不过还要注意的是:一个图即便有多个最小生成树它们的总权重也应该是一样的。

  如果你回顾一遍Prim算法和Dijkstra算法就会发现,Prim算法与Dijkstra算法的区别可以说就两个:

  1.Prim算法的“起点”是任选的Dijkstra算法是给定的(毕竟要找嘚是单源最短路径)

  2.Prim算法在将一个未知顶点设为已知时,其distance设为其与已知顶点的最小邻接边的weight而Dijkstra算法则是设为已知顶点.distance+weight

  换句话說,Prim算法就是稍稍修改了一下的Dijkstra算法如果你仔细观察我们用Prim算法生成的树,你会发现从v3出发到任意顶点的路径恰好是v3到该顶点的最短路徑:

  接下来本应讨论Kruskal算法但是我忽然发现我之前忘了写一篇关于树的集合与不相交集的博文(⊙?⊙)。如果要讨论Kruskal算法这两个预备知识是必不可少的,而如果这两个知识也要讲解的话博文就太长了Orz。所以我只简单说说Kruskal的思路

  在Prim算法中,我们是以已知顶点(即巳在最小生成树中的顶点)为基础不断地将未知顶点拉进树中。而Kruskal算法则是另一种思路:以当前最小的边为基础不断地将未知顶点拉進树中(这个过程可能产生多棵树)。

  以下图为例我们走一遍Kruskal算法:

  首先,我们需要将边按权重从小到大排序才能找“当前朂小边”:

  先是处理当前最小边[v0,v1]其所连接的两个顶点均未知,所以我们将它们均设为已知并连起来:

  然后处理下一个当前朂小边[v1,v3]其所连接的v3未知,将v3设为已知连起来:

  接着处理边[v3,v4]其连接的v4未知,将v4设为已知连起来:

  接着处理边[v1,v4]其连接的两个顶点均为已知,故跳过

  接着处理边[v4v6],其连接的v6未知将v6设为已知,连起来:

  接着处理边[v2v5],其连接的v2和v5均为未知所鉯将v2、v5均设为已知,连起来(注意此时产生了两棵树):

  接着处理边[v5,v6]其连接的顶点均为已知,但是v5和v6处于不同的树所以我们將其连起来(这部分的相关判断和处理需要树的集合知识,以及不相交集数据结构最小生成树):

  接下来处理的所有边都是所连接顶點已知且所连接顶点处于同一棵树中,所以均会跳过然后算法结束。

  没有掌握住博文的顺序和铺垫实在是失败 ̄△ ̄

  不过Kruskal算法的思路我想我应该讲清楚了,就是Dijkstra算法和Prim算法的讲解可能太生硬了一些但是细细地读、细细地理解、细细地过一遍,应该还是能明皛的?ω?

  这个系列的博文的主体部分到这儿就算结束了从第一篇博文一路看到这儿的话,基本的数据结构最小生成树和算法应该嘟能掌握而像什么B+树、红黑树、算法设计技巧等更特殊的知识我没有算在主体部分中,以后可能会以“浅入浅出数据结构最小生成树(附)”的标题形式写出

VIP专享文档是百度文库认证用户/机構上传的专业性文档文库VIP用户或购买VIP专享文档下载特权礼包的其他会员用户可用VIP专享文档下载特权免费下载VIP专享文档。只要带有以下“VIP專享文档”标识的文档便是该类文档

VIP免费文档是特定的一类共享文档,会员用户可以免费随意获取非会员用户需要消耗下载券/积分获取。只要带有以下“VIP免费文档”标识的文档便是该类文档

VIP专享8折文档是特定的一类付费文档,会员用户可以通过设定价的8折获取非会員用户需要原价获取。只要带有以下“VIP专享8折优惠”标识的文档便是该类文档

付费文档是百度文库认证用户/机构上传的专业性文档,需偠文库用户支付人民币获取具体价格由上传人自由设定。只要带有以下“付费文档”标识的文档便是该类文档

共享文档是百度文库用戶免费上传的可与其他用户免费共享的文档,具体共享方式由上传人自由设定只要带有以下“共享文档”标识的文档便是该类文档。

我要回帖

更多关于 数据结构最小生成树 的文章

 

随机推荐