1、什么是最小生成树(Minimum Spanning Tree)
- 是一棵
树
- 无回路
- |V|个顶点一定有|V|-1条边
- 是
生成
树- 包含全部顶点
- |V|-1条边都在图里
- 边的权重和
最小
最小生成树存在↔ 图连通
2、贪心算法
- 什么是“贪”:每一步都要最好的
- 什么是“好”:权重最小的边
- 需要约束:
- 只能用图里有的边
- 只能正好用掉|V|-1条边
- 不能有回路
3、Prim算法— 让一棵小树长大
void Prim()
{
MST = { s };//s维护折树中所有的顶点
while (1)
{
//dist值的含义为距离这棵树的距离,树节点dist为0;
//和树相邻节点dist为边权重,不相邻节点dist为无穷大。
V = 未收录顶点中dist最小者;
if (这样的V不存在)
break;
将V收录进MST: dist[V] = 0;//dist设为0表示收入树中
for (V 的每个邻接点W)//收录一个顶点会对该定点相邻顶点dist有影响
if (W未被收录)
if (E(V, W) < dist[W])
{
dist[W] = E(V, W);
parent[W] = V;
}
}
if (MST中收的顶点不到 | V | 个)//图不连通
Error(“生成树不存在”);
}
Prim算法中的dist[V]初始化:
dist[V] = E(s,V)或正无穷
parent[s] = -1
时间复杂度:
T = O( |V|2 ) 稠密图合算
3、Kruskal算法— 将森林合并成树
图中每一个顶点都可以看成一棵树,每次找权重最小的边,把它收进来。每次把边收进来,就把两棵树并成一棵树,最后把所有的节点并成一棵树。
void Kruskal(Graph G)
{
MST = {};//生成树集合中收集的元素为边
while (MST 中不到 |V| -1 条边&& E 中还有边) {
从E中取一条权重最小的边E(v, w);/* 最小堆*/
将E(v, w)从E中删除;
if (E(V, W)不在MST中构成回路)/* 并查集*/
将E(V, W) 加入MST;
else
彻底无视E(V, W);//生成树不要该边,E中也不要
}
if (MST 中不到 |V| -1 条边)//图不连通
Error(“生成树不存在”);
}
-
从E中取一个权重最小的边,将E中元素组织成一个
最小堆
,每次取出一条边复杂度为log(E)。 -
判断E(v,w)(表示从顶点V到顶点W的一条边)的加入会不会在最小生成树中构成回路:
使用并查集
,加边E(v,w)之前检查顶点V和W,如果二者属于不同的树,不会构成回路,在同一棵树中则会构成回路。
时间复杂度:
T = O( |E| log |E| )
对于稀疏图比较划算