最小生成树问题
设G=(V,E)是无向连通带权图,即一个网络。图中每一条边(u,v)的权是c[u][v],表示联通u与v的代价。
如果G的子图T是一棵包含G的所有顶点的树,则称T为G的生成树。生成树上各边权的总和称为该生成树的耗费。在G的所有生成树中,耗费最小的生成树称为G的最小生成树:
算法思想
用贪心算法可以设计出构造最小生成树的有效算法。
Prim和Kruskal算法都是应用贪心算法设计的。
Prim算法基本思想:
设G=(V,E)是连通带权图,V={v1,v2,…,vn}
首先置S={vx},这里vx为任意起点(vx∈V),然后重复执行下列操作:
- 只要S是V的真子集,就作出如下贪心选择
- 选取满足条件vi∈S,vj∈V-S,且c[i][j]是权值最小的边,则将顶点vj添加到S中。
- 这个过程一直进行到S=V时为止(即S中边数为n时),此时选取到的所有边恰好构成G的一颗最小生成树。
下面绘图的形式来演示Prim算法的具体过程,这里选取v1为起点,具体算法中根据Prim函数的start来决定起点。
核心伪代码
int Prim(i int start)// start为起始顶点
{
int lowcost[MAX];//最小权值
int closest[MAX];//前缀顶点
bool S[MAX];//集合S
for (初始化所有顶点)
{
lowcost[i] = cost[start][i];//其余顶点与顶点start的最小距离
closest[i] = start;
S[i] = false;//初始化集合S为空
}
S[start] = true;//顶点start加入集合S
for (遍历除start外剩余顶点)
{
找到不在集合S中的最小边权的顶点j
}
sumweight += lowcost[j];//加上权重
S[j] = true;//顶点j加入S集合
for (遍历所有点)
if ((!S[k]) && (cost[j][k] < lowcost[k])) //若有更小的权重
{
/更新lowcost和closest
}
return sumweight;//返回权重和
}
算法复杂度分析
Prim算法的时间复杂度与网中的边数无关,适合于稠密图,时间复杂度O(n²),但可以用堆优化,优化后的Prim算法时间复杂度O(mlogn)。