最小生成树
构成生成树的准则有三条:
<1> 必须只使用该网络中的边来构造最小生成树。
<2> 必须使用且仅使用n-1条边来连接网络中的n个顶点
<3> 不能使用产生回路的边。
普里姆(Prim)算法
以某顶点为起点,逐步找各各顶点上最小权值的边来构建最小生成树
1. 代码
void MiniSpanTree_Prim(MGraph G)
{
int min, i, j , k;
int adjvex[MAXVEX]; //保存相关顶点下标
int lowcost[MAXVEX]; //保存相关顶点间边的权值
lowcost[0] = 0; //v0作为最小生成树的根开始遍历,权值为0 凡是lowcost数组的值被设置为0则表示纳入最小生成树
adjvex[0] = 0; //v0第一个加入
for(i = 1; i < G.numVertexes; i++)
{
lowcost[i] = G.arc[0][i]; //将邻接矩阵第0行所有权值先加入数组
adjvex[i] = 0; //初始化全部先为v0的下标
}
//真正构造最小生成树的过程
for(i = 1; i < G.numVertexes; i++)
{
min = INFINITY; //初始化最小权值为65535等不可能权值
j = 1;
k = 0;
//遍历全部顶点
while(j < G.numVertexes)
{
//找出lowcost数组已存储的最小权值
if(lowcost[j] != 0 && lowcost[j] < min)
{
min = lowcost[j];
k = j; //将发现的最小权值的下标存入k 以便使用
}
j++;
}
//打印当前顶点边中权值最小的边
printf("(%d, %d)", adjvex[k], k);
lowcost[k] = 0; //将当前顶点的权值设置为0, 表示顶点已经完成任务,进行下一个顶点的遍历
//邻接矩阵k行逐个遍历全部顶点
for(j = 1; j < G.numVertexes; j++)
{
if(lowcost[j] != 0 && G.arc[k][j] < lowcost) //lowcost[j] != 0表示已经是生成树的顶点不参与最小权值的查找
{
lowcost[j] = G.arc[k][j];
adjvex[j] = k;
}
}
}
}
2. 解析
简单描述
<1> 设一个有n个顶点的连通网络为G(V,E),最初先构造一个只有n个顶点,没有边的非连通图T={V,空},图中每个顶点自成一格连通分量。
<2> 在E中选择一条具有最小权植的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则,即这条边的两个顶点落到同一连通分量 上,则将此边舍去(此后永不选用这条边),重新选择一条权植最小的边。
<3> 如此重复下去,直到所有顶点在同一连通分量上为止。
详解
克鲁斯卡尔(Kruskal)算法
从边出发,直接去找最小权值的边来构建最小生成树,构建时要考虑是否会形成环路
1. 代码
typedef struct Edge
{
int begin;
int end;
int weight;
}Edge;
int Find(int *parent, int f) //查找连线顶点的尾部下标
{
while(parent[f] > 0)
{
f = parent[f];
}
return f;
}
//生成树
void MiniSpanTree_Kruskal(MGragh G)
{
int i, n, m;
Edge edges[MAXEDGE];
int parent[MAXVEX]; //定义一维数组用来判断边与边是否形成回路
//此处省略将邻接矩阵G转化为边集数组edges并由小到大排序的代码
for(i = 0; i < G.numVertexes; i++)
{
parent[i] = 0;
}
for(i = 0; i < G.numEdges; i++)
{
n = Find(parent, edges[i].begin);
n = Find(parent, edges[i].end);
if(n != m) //不等 则表明没有形成回路
{
parent[n] = m; //将此边的结尾顶点放入下标为起点的parent中,表示此顶点已经在生成树集合里
printf("(%d, %d) %d", edges[i].begin, edges[i].end, edges[i].weight);
}
}
}
2. 解析
简单概述
①记Graph中有v个顶点,e个边
②新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
③将原图Graph中所有e个边按权值从小到大排序
④环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中 如果这条边连接的两个节点于图Graphnew中不在同一个连通分 添加这条边到图Graphnew中
详解
总结
普里姆算法主要针对顶点展开,即边数非常多的情况下更好,适合稠密图
克鲁斯卡尔算法主要针对边,边数少效率更高,适合稀疏图