浅谈最小生成树

来源:互联网 发布:微信菜单事件推送 php 编辑:程序博客网 时间:2024/06/15 18:12

什么是最小生成树

最小生成树是一副连通加权无向图中一颗权值最小的生成树

最小生成树其实是最小权重生成树的简称。

一个连通图可能有多个生成树。当图中的边具有权值时,总会有一个生成树的边的权值之和小于或者等于其它生成树的边的权值之和。广义上而言,对于非连通无向图来说,它的每一连通分量同样有最小生成树。

以有线电视电缆的架设为例,若只能沿著街道布线,则以街道为边,而路口为顶点,其中必然有一最小生成树能使布线成本最低。
简单点说有几个城市你要设计一个路线,这个路线能走完所有的这几个城市,而且路程最短,这个路线就是最小生成树的含义。

相关性质

存在个数

最小生成树在一些情况下可能会有多个。例如,当图的每一条边的权值都相同时,该图的所有生成树都是最小生成树。
如果图的每一条边的权值都互不相同,那么最小生成树将只有一个[1]。这一定理同样适用于最小生成树森林。

边的权值之和最低的子图

如果图的边的权值都为正数,那么最小生成树就是该图的所有包含所有顶点的子图中权值最低的子图。

环定理

对于连通图中的任意一个环 C:如果 C中有边 e的权值大于该环中任意一个其它的边的权值,那么这个边不会是最小生成树中的边

最小权值边

如果图的具有最小权值的边只有一条,那么这条边包含在任意一个最小生成树中。

算法介绍与代码实现

Kreskal

简介

Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪心算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。

步骤

1.新建图G,G中拥有原图中的相同的节点,但是没有边

2.将原图中所有边升序排列

3.从权值最小的边开始,如果这两条边连接的两个节点于图G中不再一个两桶分量中,则添加这个边到图G中

4.重复步骤3,直到所有节点都在同一个连通分量内

证明

1.这样的步骤保证了选取的每条边都是桥,因此图G构成一个树。

2.为什么这一定是最小生成树呢?关键还是步骤3中对边的选取。算法中总共选取了n-1条边,每条边在选取的当时,都是连接两个不同的连通分量的权值最小的边

3.要证明这条边一定属于最小生成树,可以用反证法:如果这条边不在最小生成树中,它连接的两个连通分量最终还是要连起来的,通过其他的连法,那么另一种连法与这条边一定构成了环,而环中一定有一条权值大于这条边的边,用这条边将其替换掉,图仍旧保持连通,但总权值减小了。也就是说,如果不选取这条边,最后构成的生成树的总权值一定不会是最小的。

时间复杂度

O(Elog2E)E为边的

代码实现

KRUSKAL-FUNCTION(G, w)1    F := 空集合2    for each 图 G 中的顶点 v3        do 將 v 加入森林 F4    所有的边(u, v) ∈ E依权重 w 递增排序5    for each 边(u, v) ∈ E6        do if u 和 v 不在同一棵子树7            then F := F ∪ {(u, v)}8                將 u 和 v 所在的子树合并

Prim

简介

从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。

1.输入:一个加权连通图,其中顶点集合为V,边集合为E;

2.初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};

3.重复下列操作,直到Vnew = V:

(1.在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则是V中没有加入Vnew的顶点(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则是V中没有加入Vnew的顶点(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
(1将v加入集合Vnew中,将(u, v)加入集合Enew中;

4.输出:使用集合Vnew和Enew来描述所得到的最小生成树。

时间复杂度

邻接矩阵、搜索 O(V2)

邻接表 O(E + V log(V))

代码实现

for(int i = 1;i <= n; i++) {        int min_1 = INF;        int next;        for(int j = 1;j <= n; j++) {            if(!vis[j] && dis[j]<min_1) {                min_1 = dis[j];                next = j;            }        }        if(min_1 == INF) {            break;        }         ans += min_1;        vis[next] = true;        for(int j = 1;j <= n; j++) {            if(!vis[j] && dis[j]>G[next][j]) {                dis[j] = G[next][j];            }        }    }