最小生成树(Minimum Spanning Tree)问题
对于一个无向连通图G=(V,E),找出一个无回路的子集T包含于E,连接了所有的顶点,且其权值之和
为最小。由于T无回路且连接了所有的顶点,因而必然是一棵树,即生成树,又由于权值和最小,因而叫做最小生成树。确定图G的树T的问题成为最小生成树问题。如下图是图G,阴影边是最小生成树的边,可知权值和为37.
最小生成树一般算法
GENERIC - MST(G)
{
A <- NULL;
while A is not a MST
{
find an edge(u, v) has the minimum weight is safe for A;
insert (u,v) to A;
}
return A;
}
安全边(safe edge),顾名思义就是在当前剩下的边中具有最小权值,且加入MST后不会形成环的边。具体算法的实现就在于如何筛选安全边。根据筛选安全边的不同,一般有两种最为常用的算法,Kruskal和Prim算法,它们都是贪心算法。
Kruskal算法
kruskal算法时贪心策略的运用。将所有边按照权值从小到大排序,然后顺序扫描这些边,只要是安全的即将边加入MST中。判断是否安全的准则是边两端点是否有同一个祖先,采用不相交数据集合可以很快查得。下面是其算法代码:
MST-KRUSKAL(G)
{
A <- NULL;
for each vertex v in V[G]
MAKE-SET(V);
sort all edges of E into nondecreasing order by weight;
for every edge(u,v) in E,taken in nondecreasing order by weight
if FIND-SET(u) != FIND-SET(v)
{
insert (u, v) to A;
UNION(u, v);
}
return A;
}
时间复杂度O(ElgE),由于是连通图,故|V| - 1 <= E <= |V|^2,所以lgE = O(lgV),故时间也可表述为O(ElgV)。具体实现代码如下,后面将会给出用到的所有代码,相关约定请参看图的基本算法(一)。
size_t AGraph::kruskal(AGraph *mst)
{//克鲁斯卡尔算法求最小生成树,返回最小权值和,最小生成树记录在mst中
struct findRoot:public binary_function<vector<size_t>,size_t,size_t>
{//局部函数对象类,用于查询并查集
size_t operator()(const vector<size_t> &UFS, size_t v)const
{
while (v != UFS[v]) v = UFS[v];
return v;
}
};