在一个有权连通图(网络)中,生成树的各边权值之和称为生成树的代价。在网络的所有生成树中,权值最小的那颗生成树称为最小代价生成树(Minimum Cost Spanning Tree),简称为最小生成树Minimum Spanning Tree)。讨论问题就是如何寻找一颗各边权的总和最小的生成树。
Q:假设n个城市之间建立公路网,则连通n个城市只需要n-1条公路。如何在最节省经费条件下构建此公路网。对于诸如此类问题都可以使用一个具有n个顶点的无向网络来表示。由于网路的每个生成树正好是n-1条边,这样的问题就转化为如何选择n-1条边使它们形成一个最小生成树。
目前,采用贪心思想来求解最小生成树的方法主要有Kruskal(克鲁斯卡尔算法)和Prim(普里姆算法)
Kruskal算法
是一种按照网络中各边的权值递增的顺序构造出 最小生成树的方法。其基本思想是设无向连通网为G=(V,E),令G的最小生成树为T,其初态为T(V,{}),即开始时,最小生成树T是由图G中的n个顶点构成,顶点之间没有一条边,这样T中各个顶点各自构成一个连通分量。然后按照边的权值由小到大的顺序,考察G的边界E的各条边。并选择权值最小且不与T中的边构成环的一条边加入到最小生成树的边集合中(这一条是Kruskal算法所使用的贪心选择准则),如此下去,直到所有的结点都加入到最小生成树的结点集合中为止,Kruskal算法求解过程如下。
1)初始化
将图的边按权值大小进行排序。通常使用最小堆来存放图中的所有边,堆中的每个结点的内容包括一条边的起点、终点和代价。
2)求代价最小的边
在构造最小生成树过程中,利用并查集的运算检查依附一条边的两顶点是否在同一连通分量(即并查集的同一个子集合)上,如果是则舍去这条边;否则将此边加入T中,同时将这两个顶点放在同一个连通分量上。
并查集作为一种高效易实现的数据结构提供看集合间的合并操作,即将两个并查集合并成一个并查集;同时提供了集合元素的归属查找操作,即返回待查找元素所在集合名字。
最常见的并查集实现是森林,即在森林中,每棵树代表一个集合,用树根来标识一个集合。当要把两个集合S1和S2合并时,只需将S1的根的父亲设置为S2的根就可以。当查找一个元素X时,只需要沿着叶子到根节点的路径找到X所在树的 根节点,就确定了X所在的集合。
3)合并
随着各边逐步加入到最小生成树T的边集合中,各连通分量也逐渐合并,直到形成一个连通分量为止。
Prim算法
基本思想是设无向连通图G=(V,E),令G的最小生成树为T=(U,TE),其中U是G的生成树的顶点集合,TE是G的生成树中边的集合。开始时,U={u0},TE={}。u0是G中的任意一个顶点。然后重复进行如下操作:在一个顶点在U中,另一个顶点不在U中的所有边中选择权值最小的边(u,v),把它的顶点加入到集合U中(这是prim算法所使用的贪心算法)。如此继续下去,直到网络中的所有顶点都加入到生成树顶点集合U中为止,Prim算法的求解过程如下:
1)初始
T只包含根结点s,U={s}。对于任意结点v∈V-U,令 closedge[v].lowcost=cosr(s,v) , closedge[v].adjvex=s.
2)选择
选择结点u∈V-U,使closedge[u].lowcost=min{closedge[v].lowcost | v∈ V-U}。将结点u及边< closedge[u].adjvex,u>加入数T,令U=U ∪ {u}。
3)修改
对于任意结点v∈V-U,如果cost[u,v] < closedge[v].lowcost,则closedge[v].lowcost=cost[u,v];closedge[v].adjvex=u.
4)判断
若U=V,则算法结束,否则转第2)步。
其中,数组closedge表示的是一个从顶点集U到V-U的代价最小的生成树,数组cost代表两个顶点之间的代价。