无向连通图的最小生成树
设G=(V,E)是一个无向连通图,生成树上各边的权值之和称为该生成树的代价,在G的所有生成树中,代价最小的生成树即为其最小生成树。
Prim(普里姆)算法的基本思想是:
设 G=(V,E) 是一个无向连通图,令T=(U,TE) 是 G 的最小生成树。T的初始状态为 U={v0}(v0∈V),TE={},然后重复执行下述操作:
在所有 u∈U,v∈V-U 的边中找一条代价最小的边(u,v) 并入 U,直至 U=V 为止。此时TE中必有 n-1 条边,T 就是最小生成树。
代码如下:
void MiniSpanTree(MGraph G)
{
int min,i,j,k;
int adjvex[MAXVEX];//保存相关顶点下标
int lowcost[MAXVEX];//保存相关顶点间边的权值
adjvex[0] = 0;//初始化第一个顶点下标为0
lowcost[0] = 0;//初始化第一个权值为0,即Vo加入生成树
//lowcost的值为0,表示此下标的顶点已经加入生成树
for (i = 1; i < G.vertexNum; i++)//循环除0外的全部顶点
{
lowcost[i] = G.arc[0][i];//将Vo顶点与之有边的权值存入数组
adjvex[i] = 0;//初始化都为Vo的下标
}
for (i = 1; i < G.vertexNum; i++)
{
min = INFINITE;//初始最小值为极大∞
j = 1;
k = 0;
while (j < G.vertexNum)//循环全部顶点
{
if (lowcost[j] != 0 && lowcost[j] < min)//如果权值不为0且小雨min
{
min = lowcost[j];//让当前权值成为最小值
k = j;//将当前最小值的下标存入k
}
j++;
}
printf("(%d,%d)",adjvex[k],k);//输出当前顶点边中权值最小边
lowcost[k] = 0;//0表示该顶点已完成
for (j = 1; j < G.vertexNum; j++)//循环所有顶点
{
//若下标为k的顶点的各边权值小于此前这些顶点未被加入生成树的权值
if (lowcost[j] != 0 && G.arc[k][j] < lowcost[j])
{
lowcost[j] = G.arc[k][j];//将较小权值存入lowcost
adjvex[j] = k;//将下标为k的顶点存入adjvex
}
}
}
}
Kruskal(克鲁斯卡尔)算法的基本思想是:
基本思想:设无向连通网为 G=(V, E),令 G 的最小生成树为 T=(U, TE),其初态为 U=V,TE={ },然后,按照边的权值由小到大的顺序,考察 G 的边集E中的各条边。若被考察的边的两个顶点属于T的两个不同的连通分量,则将此边作为最小生成树的边加入到T中,同时把两个连通分量连接为一个连通分量;若被考察边的两个顶点属于同一个连通分量,则舍去此边,以免造成回路,如此下去,当T中的连通分量个数为1时,此连通分量便为 G 的一棵最小生成树。
代码如下:
/对边集数组Edge结构的定义
typedef struct
{
int begin;
int end;
int weight;
}Edge;
void MiniSpanTree(MGraph G)
{
int i,n,m;
Edge edges[MAXEDGE];//定义边集数组
int parent[MAXVEX];//定义一数组用来判断边与边是否形成环路
//此处省略将邻接矩阵G转化为边集数组edges并按权值由小到大排序的代码
for (i = 0; i < G.vertexNum; i++)
parent[i] = 0;//初始化为0
for (i = 0; i < G.edgeNum; i++)//循环每一条边
{
n = Find(parent,edges[i].begin);
m = Find(parent,edges[i].end);
if (n != m)//假如n与m不相等,说明此边没有与现有的生成树形成环路
{
parent[n] = m;//将此边的结尾顶点放入下标为起点的parent中,表示此顶点已经在生成树中
printf("(%d,%d) %d ",edges[i].begin,edges[i].end,edges[i].weight);
}
}
}
int Find(int * parent,int x)//查找连线顶点的尾部下标
{
while (parent[x] > 0)
x = parent[x];
return x;
}