《算法导论》笔记__最小生成树

最小生成树的形成

假定有一无向图 G = ( V , E ) G=(V,E) G=(V,E)和权重函数 w w w, 我们希望找出图 G G G的一颗最小生成树。

我们使用贪心算法来解决这个问题。贪心算法管理一个遵守下述循环不变式的边集和 A A A: 每轮循环之前, A A A是最小生成树的一个子集。在当轮循环,我们要做的就是选择一条边 ( u , v ) (u,v) (u,v),将其加入子集 A A A中,使 A A A仍然为最小生成树的一个子集,我们称这样的边为安全边

找出最小生成树的关键在于如何选择安全边。在介绍具体的方法之前,我们先引入一些定义。无向图 G = ( V , E ) G=(V,E) G=(V,E)的一个切割 ( S , V − S ) (S,V-S) (S,VS)是对集合 V V V的一个划分。如果一条边 ( u , v ) (u,v) (u,v)的一个端点位于集合 S S S,令一个端点位于集合 V − S V-S VS,则称这条边横跨切割 ( S , V − S ) (S,V-S) (S,VS)。如果集合 A A A中不存在横跨切割 ( S , V − S ) (S,V-S) (S,VS)的边,则称该切割尊重集合 A A A。在横跨切割的所有边中,称权重最小的边为轻量级边

下面我们将证明:假如切割 ( S , V ) (S,V) (S,V)尊重集合 A A A,则横跨切割的轻量级边 ( u , v ) (u,v) (u,v)对集合 A A A是一条安全边。设 T T T是一颗包含 A A A的最小生成树,且T不包含边 ( u , v ) (u,v) (u,v)。边 ( u , v ) (u,v) (u,v)与生成树 T T T u , v u,v u,v之间的简单路 p p p构成了一条回路。由于 ( u , v ) (u,v) (u,v)横跨切割,则 p p p中必存在一条边 ( x , y ) (x,y) (x,y)横跨切割。我们设置一个新的生成树 T ′ = T ∪ ( u , v ) − ( x , y ) T'=T\cup{(u,v)-(x,y)} T=T(u,v)(x,y)。由于 ( u , v ) (u,v) (u,v)为轻量级边,所以有 w ( u , v ) ≤ w ( x , y ) w(u,v)\le w(x,y) w(u,v)w(x,y),于是有 w ( T ′ ) = w ( T ) − w ( x , y ) + w ( u , v ) ≤ w ( T ) w(T')=w(T)-w(x,y)+w(u,v)\le w(T) w(T)=w(T)w(x,y)+w(u,v)w(T)。由于 T T T是最小生成树,所以 T ′ T' T也是最小生成树。由于 A ⊆ T A\subseteq T AT ( x , y ) ∉ A (x,y)\notin A (x,y)/A,所以 A + ( u , v ) ⊆ T ′ A+(u,v)\subseteq T' A+(u,v)T。由此证明, ( u , v ) (u,v) (u,v)对集合 A A A是一条安全边。

随着最小生成树算法的推进, A A A总保持着无环的状态。在算法执行的任意时刻,图 G A = ( V , A ) G_A=(V,A) GA=(V,A)是一个森林,图 G A G_A GA中的每一个连通分量是一颗树。所有对 A A A的安全边 ( u , v ) (u,v) (u,v)所连接的是 G A G_A GA中不同的连通分量。

Kruskal算法

Kruskal找到安全边的方法是,在所有连接森林中不同的两棵树的边中,找到权重最小的一个边 ( u , v ) (u,v) (u,v),时间复杂度为 O ( E log ⁡ V ) O(E\log V) O(ElogV)。伪代码如下:

MST-KRUSKAL(G,w)
	A = 空集
	for each vertex v in G.V
		MAKE-SET(v)
	sort the edges of G.E into nondecreasing order by weight w
	for each edge(u,v) in G.E, taken in nondecreasing order by weight
		if FIND-SET(u) != FIND-SET(v)
			A = A∪{(u,v)}
			UNION(u,v)
	return A

FIND-SET(u)用来返回集合 u u u的代表元素,UNION用来合并两个集合。

Prim算法

Prim算法中的集合 A A A总是构成一棵树,这棵树从一个任意的根节点 r r r开始,一直长大到覆盖 V V V中的所有结点为止。算法每一步在连接集合 A A A A A A之外结点的边中,选择一条轻量级边加入 A A A中。伪代码如下:

MST-PRIM(G,w,r)
	for each u in G.V
		u.key = ∞
		u.pi = NIL
	r.key = 0
	Q = G.V
	while Q != 空集
		u = EXTRACT-MIN(Q)
		for each v in G.Adj[u]
		if v in Q and w(u,v) < v.key
			v. pi = u
			v.key = w(u,v)

在算法的执行过程中,所有不在 A A A中的结点都存放在一个基于 k e y key key属性的最小优先队列中 Q Q Q中。对每个结点 v v v,属性 v . k e y v.key v.key保存的是连接树 A A A中结点的所有边中的最小边的权重。

如果使用二叉堆,时间复杂度为 O ( E log ⁡ V ) O(E\log V) O(ElogV);如果使用斐波那契堆,时间复杂度为 O ( E + V log ⁡ V ) O(E+V\log V) O(E+VlogV)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值