最小生成树,秒懂!(一)

joytrian
发布于 2022-7-6 17:05
浏览
0收藏

 

前言
在数据结构与算法的图论中,(生成)最小生成树算法是一种常用并且和生活贴切比较近的一种算法。但是可能很多人对概念不是很清楚,什么是最小生成树?

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。
通俗易懂的讲就是最小生成树包含原图的所有节点而只用最少的边和最小的权值距离。因为n个节点最少需要n-1个边联通,而距离就需要采取某种策略选择恰当的边。

学习最小生成树实现算法之前我们要先高清最小生成树的结构和意义所在。咱么首先根据一些图更好的祝你理解。

一个故事
在城市道路规划中,是一门很需要科学的研究(只是假设学习不必当真)。在公路时代城市联通的主要矛盾是时间慢,而造价相比运输时间是次要矛盾。所以在公路时代我们尽量使得城市能够直接联通,缩短城市联系时间,而稍微考虑建路成本!随着科技发展、高级铁路、信息传输相比公路运输快非常非常多,从而事件的主要矛盾从运输时间转变为造价成本,故有时会关注联通所有点的路程(最短),这就用到最小生成树算法。

城市道路铺设可能经历以下几个阶段。

  • 初始,各个城市没有高速公路(铁路)。
  • 政府打算各个城市铺设公路(铁路),每个城市都想成为交通枢纽,快速到达其他城市,但每个城市都有这种想法,如果实现下去造价太昂贵。并且造成巨大浪费。
  • 最终国家选择一些主要城市进行联通,有个别城市只能稍微绕道而行,而绕道太远的、人流量多的国家考虑新建公路(铁路),适当提高效率。
     最小生成树,秒懂!(一)-鸿蒙开发者社区

不过上面铁路规划上由于庞大的人口可能不能够满足与"有铁路"这个需求,人们对速度、距离、直达等条件一直在追求中……

但是你可以想象这个场景:有些东西造假非常非常昂贵,使用效率非常高,我这里假设成黄金镶钻电缆 铺设,所以各个城市只要求不给自己落下,能通上就行(没人敢跳了吧)。

要从有环图中选取代价和最小的路线一方面代价最小(总距离最小最省黄金)另一方面联通所有城市。

然而根据上图我们可以得到以下最小生成树,但是最么生成这个最小生成树,就是下面要讲的了。

最小生成树,秒懂!(一)-鸿蒙开发者社区

而类似的还有局部区域岛屿联通修桥,海底通道这些高成本的都多多少少会运用。

Kruskal算法
上面介绍了最小生成树是什么,现在需要掌握和理解最小生成树如何形成。给你一个图,用一个规则生成一个最小生成树。而在实现最小生成树方面有prim和kruskal算法,这两种算法的策略有所区别,但是时间复杂度一致。

Kruskal算法,和前面讲到的并查集关系很大,它的主要思想为:

先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。
简而言之,Kruskal算法进行调度的单位是边,它的信仰为:所有边能小则小,算法的实现方面要用到并查集判断两点是否在同一集合。

而算法的具体步骤为:

  1. 将图中所有边对象(边长、两端点)依次加入集合(优先队列)q1中。初始所有点相互独立。
  2. 取出集合(优先队列)q1最小边,判断边的两点是否联通。
  3. 如果联通说明两个点已经有其它边将两点联通了,跳过,如果不连通,则使用union(并查集合并)将两个顶点合并,这条边被使用(可以储存或者计算数值)。
  4. 重复2,3操作直到集合(优先队列)q1为空。此时被选择的边构成最小生成树。
     最小生成树,秒懂!(一)-鸿蒙开发者社区

最小生成树,秒懂!(一)-鸿蒙开发者社区

Prim算法
除了Kruskal算法以外,普里姆算法(Prim算法)也是常用的最小生成树算法。虽然在效率上差不多。但是贪心的方式和Kruskal完全不同。

prim算法的核心信仰是:从已知扩散寻找最小。它的实现方式和Dijkstra算法相似但稍微有所区别,Dijkstra是求单源最短路径,而每计算一个点需要对这个点重新更新距离,而prim不用更新距离。直接找已知点的邻边最小加入即可!prim和kruskal算法都是从边入手处理。

对于具体算法具体步骤,大致为:

  1. 寻找图中任意点,以它为起点,它的所有边V加入集合(优先队列)q1,设置一个boolean数组bool[]标记该位置(边有两个点,每次加入没有被标记那个点的所有边)。
  2. 从集合q1找到距离最小的那个边v1并 判断边是否存在未被标记的一点`p` ,如果p不存在说明已经确定过那么跳过当前边处理,如果未被标(访问)记那么标记该点p,
  3. 并且与p相连的未知点(未被标记)构成的边加入集合q1, 边v1(可以进行计算距离之类,该边构成最小生成树) .
    重复1,2直到q1为空,构成最小生成树 !
    大体步骤图解为:

最小生成树,秒懂!(一)-鸿蒙开发者社区最小生成树,秒懂!(一)-鸿蒙开发者社区
因为prim从开始到结束一直是一个整体在扩散,所以不需要考虑两棵树合并的问题,在这一点实现上稍微方便了一点。

当然,要注意的是最小生成树并不唯一,甚至同一种算法生成的最小生成树都可能有所不同,但是相同的是无论生成怎样的最小生成树:

  • 能够保证所有节点连通(能够满足要求和条件)
  • 能够保证所有路径之和最小(结果和目的相同)
  • 最小生成树不唯一,可能多样的
     最小生成树,秒懂!(一)-鸿蒙开发者社区

 

文章转自公众号:bigsai

标签
已于2022-7-6 17:05:37修改
收藏
回复
举报
回复
    相关推荐