根据图的深度优先遍历和广度优先遍历,可以用最少的边连接所有的顶点,而且不会形成回路。这种连接所有顶点并且路径唯一的树型结构称为生成树或扩展树。实际中,希望产生的生成树的所有边的权值和最小,称之为最小生成树。
常见的最小生成树算法有Kruskal算法和Prim算法。
Kruskal算法每次选取权值最小的边。然后检查是否加入后形成回路,如果形成回路则需要放弃。最终构成最小生成树。n个顶点的图最小生成树步骤如下:
边的权值升序排序;
选取所有未遍历的边中权值最小的边,判断加入后是否形成回路,若形成回路,放弃之,重新从未被遍历的边中选择。
重复上述步骤,直到选中n-1条边。
/*****************Kruskal算法********************/
struct Edge
{
int v1,v2;//顶点
int w;//边v1--v2权值
struct Edge *next;//指向下一条边
}
//h为按边的权值升序排序的单链表
int Kruskal(Edge *h, int *visited)
{
int edgenum = 0;//记录生成树中边的个数
int weight = 0;//权值和
Edge *p = h;
printf("最小生成树:(顶点1,顶点2,权值)\n");
while(edgenum != maximum)//maximum=顶点数,当边数=顶点数-1时结束
{
if(visited[p->v1] == 0 ||visited[p->v2]==0)
//新增边至少有一个顶点没有被访问过
{
printf("(%d,%d,%d)->",p->v1,p->v2,p->w);
weight = weight + p->w; //权值和累加
visited[p->v1] = 1;
visited[p->v2] = 1;
edgenum++;//边数+1
}
p = p->next;
if(p==NULL)//无边可加入
{
printf("spanning tree fail\n");
break;
}
}
return weight;
}
相比于Kruskal选边生成,Prim算法选择顶点生成最小生成树。
从某个顶点v开始,列出顶点 v 所有邻接点的边 选择权值最小的边(vi-->vj)加入到最小生成树中,并标记该边已被访问过;
再从vj开始 列出顶点vj所有邻接点的边,从中选择所有未被访问过的边中权值最小的边 vj-->vk 加入到最小生成树中,并标记该边已被访问过。
重复上述操作,直到找到n-1条边为止。
下面直接贴代码:
/*******************Prims算法******************/
struct Edge
{
int v1,v2;//顶点
int w;//边v1--v2权值
int marked;//标识该边是否已经被添加到最小生成树中
struct Edge *next;
}
//h为边节点构成的链表,index表示开始顶点
void Prim(Edge *h,int * visited, int index)
{
Edge *p,*min;//min每次指向剩余边中中权值最小且 与上一个边共享顶点v1
int i;
int edgenum =0;//已连接边数
int weight =0;//权值
int vertex;
min = (Edge*)malloc(sizeof(Edge));
/***添加第一条边*******/
min->w = h->w;//最小边权值初值(可任意指定但小于所有边的权值,当然比越大越好)
p = h;
while(p!=NULL)
{
if(p->v1 ==index && (p->w < min->w))
min = p; //找到以index开头 且权值最小的边结点
p = p->next;
}
min->marked = 1;//该边已被访问
visited[min->v1] = 1;
visited[min->v2] = 1; //该边两个顶点被访问过
edgenum++;
weight = min->w;
printf("(%d,%d,%d)->",min->v1,min->v2,min->w);//输出选中边
/***添加其余边*******/
while(edgenum != maximum)
{
min->w = h->w;
p = h;
while(p != NULL)
{
if(p->marked==0 && visited[p->v1]+visited[p->v2]==1)//边没有被访问过 且有且只有一个顶点被访问,另一顶点没有被访问过
if(p->w < min->w)
min = p; //找到权值最小的边
p = p->next;
}
min->marked = 1;
visited[min->v1] =1;
visited[min->v2] =1;
edgenum++;
weight += min->w;
printf("(%d,%d,%d)->",min->v1,min->v2,min->w);
}
printf("\n总权值为:%d",weight);
}