最小生成树(MST)

一、基本概念

注意:我们讨论的最小生成树问题是对于连通无向图的。

生成树:连通无向图G=\left ( V,E \right )边集E的一个子集T,若T无环的,并且连通所有的结点(此时T一定为一棵树),则称为G的生成树。

最小生成树:G生成树中,权重最小的。

切割:无向图G=\left ( V,E \right )的一个切割\left ( S,V-S \right )是点集V的一个划分,如图一所示。

横跨:如果一条边\left ( u,v \right )\in E的一个端点位于集合S另一个端点位于集合V-S,则称该条边横跨切割\left ( S,V-S \right )

尊重:如果集合A中不存在横跨该切割的边,则称该切割尊重集合A

轻量级边:在横跨一个切割的所有边中,权重最小的边称为轻量级边。

图一        黑色结点位于集合S,白色结点位于集合V-S,横跨该切割的边是那些连接白色结点和黑色结点的边,其中边(d,c)是唯一轻量级边。加阴影的边属于子集A,则该切割尊重集合A。

安全边:设边集合A是图的某棵最小生成树的一个子集,若将边\left ( u,v \right )\in E加入到集合A中使得A\cup \left \{ \left ( u,v \right ) \right \}也是某棵最小生成树的子集,则称边\left ( u,v \right )A的安全边。

生成最小生成树的基本框架是:初始化A=\varnothing,然后不断向A中加入安全边直到生成最小生成树。(不同的最小生成树算法选择安全边的策略有所不同。)伪代码如下:

定理:G=\left ( V,E \right )是一个在边E上定义了实数值权重函数\omega连通无向图。设集合AE的一个子集,且A包括在图G的某棵最小生成树中,设\left ( S,V-S \right )是图G尊重集合A的任意一个切割,又设\left ( u,v \right )是横跨切割\left ( S,V-S \right )的一条轻量级边。那么边\left ( u,v \right )对于集合A安全的

该定理可以更好地理解GENERIC-MST算法:随着算法的运行,集合A总是保持无环状态。在任意时刻,图G_{A}=\left ( V,A \right )是一个森林,G_{A}中每个连通分量则是一棵树。重要的是,每次添加的安全边\left ( u,v \right )所连接的是G_{A}中不同的连通分量(否则产生环)。该算法while循环共执行\left | V \right |-1次,初始时,A=\varnothingG_{A}中有\left | V \right |棵树(即每个结点是一棵树),每次循环树的数量将减少一棵直到仅剩一棵树时,算法终止,该树就是图G=\left ( V,E \right )的一棵最小生成树。

重要的是,由该定理引出的如下推论将是我们得出最小生成树算法的直接理论依据

推论:G=\left ( V,E \right )是一个在边E上定义了实数值权重函数\omega连通无向图。设集合AE的一个子集,且A包括在图G的某棵最小生成树中,并设C= \left ( V_{C},E_{C } \right )G_{A}=\left ( V,A \right )森林中的一个连通分量(树)。如果边\left ( u,v \right )是连接CG_{A}中某个其他连通分量的一条轻量级边,则边\left ( u,v \right )对于集合A是安全的。

二、Kruskal算法

算法介绍:在Kruskal算法中,集合A是一个森林,每次加入到A中的安全边是连接两个不同分量的边中权重最小的。初始每个结点均为一个连通分量,算法的过程是不断找到连接不同连通分量中最小的边,则由推论可保证正确性。等价的实现过程是,首先按权值大小对边进行排序,按从小到大的顺序检查边,如果连接不同连通分量则保留,如果在同一连通分量中则忽略。

伪代码:

解释:我们使用不相交集合数据结构实现不同连通分量的表示。初始时,MAKE-SET为每个结点创造一个集合。FIND-SET检查uv是否属于同一连通分量(不相交集合),若不属于,则UNION合并uv所在连通分量。

时间复杂度:O(ElgV)

复杂度分析:运行时间依赖于不相交集合数据结构的实现,我们使用不相交集合森林实现,并运用按秩合并和路径压缩两个策略,这时迄今为止渐进时间最快的实现方式。第4行排序算法最快O\left ( ElgE \right )。2至8行一共执行E次FIND-SET和UNION操作和V次MAKE-SET操作,则运行时间为O\left ( \left ( V+E \right )lg^{*} V\right )。则总运行时间为O\left ( ElgE \right ),也即O\left ( ElgV \right )O\left ( lgE \right )=O\left ( lgV \right ))。

举例:

三、Prim算法

算法介绍:在Prim算法中,集合A是一棵树,每次加入到A中的安全边是连接AA之外某个结点中的最小边,初始时A中包含任意一个结点。算法实现过程是,将不在A中的结点都存放在一个基于key属性的最小优先队列Qv.key保存的是结点v连接树A的所有边的最小权重。v.\pi保存的是v在树中的父结点。

伪代码:

解释:r为我们任意选择的根结点,将r外的结点key值初始化为\infty\pi初始化为NIL,r.key设置为0以便我们第一个选择。最小优先队列Q初始化为所有结点。

时间复杂度:依赖于最小优先队列的实现:

(1)一般数组:O\left ( V^{2} \right )

(2)普通二叉堆:O\left ( ElgV \right )

(3) 斐波那契堆:O\left ( E+VlgV \right )

复杂度分析:1至4行初始化共需要O\left ( V \right )时间,11行key值更新后隐含DECREASE-KEY操作。对于第9行判断结点是否在Q中,我们可以为每个结点添加一个标记,在Q中为True,不在则为False,当结点从Q中删除时,该标记由True变为False,于是判断操作为常数时间。算法共需执行一次建堆BUILD-MIN-HEAP,V次EXTRACT-MIN,E次DECREASE-KEY。

(1)一般数组:EXTRACT-MIN为O\left ( V \right ),DECREASE-KEY为O\left ( 1 \right ),运行时间为O\left ( V^{2} +E\right ),也即O\left ( V^{2} \right )

(2)普通二叉堆:EXTRACT-MIN为O\left ( lgV \right ),DECREASE-KEY为O\left ( lgV \right ),运行时间为O\left ( \left ( V+E \right )lgV\right ),也即O\left (ElgV \right )

(3)斐波那契堆:EXTRACT-MIN为O\left ( lgV \right ),DECREASE-KEY为O\left ( 1 \right ),运行时间为O\left ( E+VlgV\right )

举例:

结点上红色数字表示d值,黑色字母表示其前驱。每次队列里最小值用蓝色圈选。

四、Boruvka算法

算法介绍:Boruvka算法是最早的计算MST的算法,它的过程类似于Kruskal和Prim的结合,其用一句话总结就是:从当前每个连通块开始向外找到连接其他连通块的最小边,直到只剩一个连通块。Boruvka算法最大的一个特点是,该算法允许并行化运行,而以上两个算法本质上都是连续的。

伪代码:

解释:F存储图G的所有顶点及发现的最小生成树中的边。count用来记录当前连通块的个数。循环检查count,当其降为1说明最小生成树已经生成。函数COUNTANDLABEL完成两件工作,不仅计算当前图F的连通块个数,还为每个结点分配其所属连通块。显然COUNTANDLABEL可用DFS或BFS运行搜索一次完成。

        函数ADDALLSAFEEDGES用来合并连通块。safe\left [ i \right ]用来记录当前第i个连通块连接其它连通块权值最小的边,所以safe数组每次只有前count个记录有意义。comp\left ( u \right )返回结点u所属的连通块标号,其值即为DFS或BFS过程中生成的森林中的第几棵树。ADDALLSAFEEDGES每次执行都遍历一遍图G的所有边集E。如果边uv连接的是同一个连通块的两个结点,则该边已经在F中生成或者不在最终的最小生成树中,不考虑即可。如果边uv连接的是不同连通块的两个结点,则检查是否可以更新safe数组。遍历结束后,将新生成的边加入到图F

时间复杂度:邻接表:O\left ( ElgV \right );邻接矩阵:O\left ( V^{2}lgV \right )

复杂度分析:我们首先分析while循环次数。每次连通块合并,至少使连通块减半,初始连通块数为V(每个顶点即一个连通块),所以循环次数为O\left ( lgV \right )。ADDALLSAFEEDGES函数显然为O\left ( V+E \right ),COUNTANDLABEL则为O\left ( V+E \right )。故复杂度为O\left ( \left ( V+E \right ) lgV\right ),即为O\left ( ElgV \right )

举例(只进行了两次循环):

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值