边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权。
最小生成树(MST):权值最小的生成树。
生成树和最小生成树的应用:要连通n个城市需要n-1条边线路。可以把边上的权值解释为线路的造价。则最小生成树表示使其造价最小的生成树。
构造网的最小生成树必须解决下面两个问题:
1、尽可能选取权值小的边,但不能构成回路;
2、选取n-1条恰当的边以连通n个顶点;
MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。
基本思想:
假设G=(V,E)是连通的,TE是G上最小生成树中边的集合。算法从U={u0}(u0∈V)、TE={}开始。重复执行下列操作:
在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0)并入集合TE中,同时v0并入U,直到V=U为止。
此时,TE中必有n-1条边,T=(V,TE)为G的最小生成树。
Prim算法的核心:始终保持TE中的边集构成一棵生成树。
看图说话:
下图展示了Prim算法查找的全过程
算法如下:
#include "GraphLink.h"
class Dist
{
public:
int index; //顶点的索引值,仅Dijkstra算法会用到
int length;//顶点之间的距离
int pre;//路径最后经过的顶点
bool operator < (const Dist &dist)
{
return length < dist.length;
}
bool operator <= (const Dist &dist)
{
return length <= dist.length;
}
bool operator > (const Dist &dist)
{
return length > dist.length;
}
bool operator >= (const Dist &dist)
{
return length >= dist.length;
}
bool operator == (const Dist &dist)
{
return length == dist.length;
}
};
void addEdgeToMST(Edge &edge,Edge* &MST,int n)// 将边e加到MST中
{
MST[n] = edge;
}
int minVertex(Graph &G,Dist * &D)// 在Dist数组中找最小值
{
int v;
for(int i = 0; i < G.verticesNum();i++)
{
if(G.mark[i] == UNVISITED)// 让v为随意一个未访问的定义
{
v = i;
break;
}
}
for(int i = 0; i < G.verticesNum();i++)
{
if(G.mark[i] == UNVISITED && D[i] < D[v])
{
v = i;// 保存目前发现的具有最小距离的顶点
}
}
return v;
}
//最小支撑树的Prim算法,
//参数G是图,参数s是开始顶点,参数MST是保存最小支撑树中所有边的数组
void prim(Graph& G, int s, Edge* &MST)
{
int MSTTag = 0;// 最小生成树边的标号
MST = new Edge[G.verticesNum() -1];
Dist *D = new Dist[G.verticesNum()];// 初始化Mark数组、D数组
for(int i = 0; i < G.verticesNum(); i++)
{
G.mark[i] = UNVISITED;
D[i].length = INFINITE;
D[i].index = i;
D[i].pre = s;
}
D[s].length = 0;
G.mark[s] = VISITED;
int v = s;
for(int i = 0; i < G.verticesNum() -1; i++)
{
if(D[v].length == INFINITE)// 非连通,有不可达顶点,
{
break;
}
// 因为v的加入,需要刷新v邻接点的D值
for(Edge edge = G.firstEdge(v); G.isEdge(edge); edge = G.nextEdge(edge))
{
if(G.mark[G.toVertex(edge)] == UNVISITED && D[G.toVertex(edge)].length > edge.weight)
{
D[G.toVertex(edge)].length = edge.weight;
D[G.toVertex(edge)].pre = v;
}
}
v = minVertex(G,D);
G.mark[v] = VISITED;
Edge edge(D[v].length,D[v].index,D[v].pre);
addEdgeToMST(edge,MST,MSTTag++);
}
}
int A[N][N] = {
// v0 v1 v2 v3 v4 v5 v6
0, 20, 0, 0, 1, 0, 0,
20, 0, 6, 4, 0, 0, 0,
0, 6, 0, 0, 0, 0, 2,
0, 4, 0, 0, 0, 12, 8,
1, 0, 0, 0, 0, 15, 0,
0, 0, 0, 12, 15, 0, 10,
0, 0, 2, 8, 0, 10, 0
};
int main()
{
GraphLink<ListUnit> graphLink(N); // 建立图
graphLink.initGraph(graphLink, A,N); // 初始化图
Edge *D;
prim(graphLink, 0, D);
for (int i = 0; i < N - 1; i ++)
{
cout << "V" << D[i].from << "->V" << D[i].to << " Weight is : " << D[i].weight << endl;
}
system("pause");
return 0;
}
运行结果: