1.问题
最小生成树:一个有 n 个结点连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
2.解析
prim算法:
**核心思想:**让一颗小树长大。
①随机选择一点v加入V(初始时为空),并设一变量ans记录已选中的权值和
②找到图中权值最小的边<v1,v2>,要求v1存在V中、v2不存在V中
③将v2加入V中,ans += 边<v1,v2>的权重
重复②③步骤
遇到的难点:
for(已选的点){
for(未选的点){
找出权值最小的边
}
将该边的权值加入总权重中
将找到的点置为已选中
}//错误思路
后来我发现这样做是行不通的,因为每加入一个新的点,都需要再重新
走一遍上面的代码,也就是说我设计的算法的时间复杂度不是O(n^2)
而是O(n^3)。
算法关键的一点是如何记录已经扫描过的边,比如我从v1开始,,经历步骤②③后将v2加入了V,
如果不做处理,步骤②又会先从v1开始扫描再扫描v2,这就会增大时间复杂度。
解决:
用三个数组并且不断更新这三个数组直到找出最小生成树
selected:记录某个顶点是否已加入生成树
T:是,F:否,初始状态下所有顶点为F。
minDist:联结已选顶点集合到未选顶点的所有边中最短的那条边
初始状态下均为inf。
parent:以selected的元素为v1,minDist的元素为边,确定parent数组的值,即parent存放的是v2。
初始状态下均为-1
图片演示
结束条件
①所有顶点都为T
②选择的边数 = 顶点数 - 1
kruskal算法:
**核心思想:**将森林合并成树
将所有边的权值按从小到大的顺序排列,若边的两顶点与选中的顶点没有构成回路,则将这两个顶点加入已选顶点中,若构成回路则跳过该边,直到所有顶点都被选中。
图片演示
3.设计
int prim()
{
随机选一个起点v,将该点的selected置为T
while(1)
{
for(v的每个未被收录的每个邻接点)
{
更新minDist和parent数组
}
for(v的每个未被收录的每个邻接点)
{
找出未收录顶点中dist最小的顶点u
}
置selected[u]为T
置v=u
if(全部顶点都已被收录)break;
}
}
void kruskal()
{
MST为空
while(MST中边数<|V|-1 && E不为空)
{
选取E中权重最小的边e
将e从E中删除
if(e在MST中不构成回路)将e加入MST
}
}
4.分析
prim算法:O(n^2)
kruskal算法:O(eloge)