最小生成树在实际中具有重要用途:
1.设计通信网
2.设计图的顶点表示城市,边表示两个城市之间通信线路,边的权值表示建造通讯线路的费用
3.n个城市之间最多可以建n(n-1)/2条线路,如何选择其中n-1条,使总造价最低
Prim算法的步骤:
⑴若从顶点v0出发构造,U={v0},TE={};
⑵先找权值最小的边(u,v),其中u∈U且v∈V-U,并且子图不构成环,则U= U∪{v},TE=TE∪{(u,v)} ;
⑶重复⑵,直到U=V为止。则TE中必有n-1条边,T=(U,TE)就是最小生成树。
我们用邻接矩阵来存储图,求最小生成树。
最后求出的最小生成树用边表存储
首先把要用的结构体都定义好
#include<stdio.h>
#include<stdlib.h>
#define MAX_VEX 10
#define INFINITY 100000
typedef int InfoType;//结点的信息,如权值、造价等
typedef char VexType;//顶点的类型
typedef int WeightType;//权值的类型
typedef enum
{
DG = 1, AG, WDG, WAG//有向图 无向图 加权有向图 加权无向图
}GraphKind;
//线性表的结构体
typedef struct VexLine
{
VexType data;//顶点的值
int indegree;//顶点的度
}VexLine;
//图的邻接矩阵的结构体
typedef struct MGraph
{
GraphKind kind;
VexLine vex[MAX_VEX];//线性表
int adj[MAX_VEX][MAX_VEX];//矩阵
int vexnum;//顶点个数
}MGraph;
typedef struct MSTEdge
{
VexType v1, v2;//边所依附的两个顶点
WeightType weight;//边的权值
}MSTEdge;
struct
{
int adjvex;//边所依附于U中的顶点的下标
int lowcost;//该边的权值
}closedge[MAX_VEX];
创建一个空图
MGraph CreateGraph()
{
int i;
int j;
MGraph G;
G.vexnum = 0;
G.kind = WAG;
for (i = 0; i < MAX_VEX; i++)
{
G.vex[i].indegree = 0;
}
for (i = 0; i < MAX_VEX; i++)
{
for (j = 0; j < MAX_VEX; j++)
{
G.adj[i][j] = INFINITY;
}
}
printf("haha success!\n");
return G;
}
定位结点是否存在
若存在返回1,若不存在返回-1
int LocateVex(MGraph *G, VexType u)
{
int i;
int a = -1;
for (i = 0; i < G->vexnum; i++)
{
if (G->vex[i].data == u)
{
a = 1;
break;
}
}
return a;
}
在图中插入一个结点
void InsertVex(MGraph *G)
{
int res = 0;
if (G->vexnum + 1 == MAX_VEX)
{
printf("The graph is overflow!\n");
}
else
{
VexType u = '\0';
printf("please enter data :\n");
getchar();
scanf("%c", &u);
res = LocateVex(G, u);
if (res != 1)
{
G->vex[G->vexnum].data = u;
G->vex[G->vexnum].indegree = 0;
G->vexnum++;
}
else
{
printf("insert vertex fail.\n");
}
}
}
在图中插入一条边
void InsertArc(MGraph *G)
{
int tail;
int head;
int weight;
printf("please enter tail:\n");
scanf("%d", &tail);
printf("please enter head:\n");
scanf("%d", &head);
printf("please enter weight\n");
scanf("%d", &weight);
if (head < G->vexnum && tail < G->vexnum)
{
G->adj[tail][head] = weight;
G->adj[head][tail] = weight;
printf("success\n");
}
else
{
printf("fail\n");
}
}
重点来了,接下来是最小生成树的Prim算法
MSTEdge* PrimMiniSpanTree(MGraph G, int u) {
//TE是存放最小生成树的n-1条边的数组指针
MSTEdge *TE;
TE = (MSTEdge *)malloc((G.vexnum - 1)*sizeof(MSTEdge));
int i;
int j;
int v;
int k;//权值最小的下标
int min;//最小的权值
//初始化closedge
for (i = 0; i < G.vexnum; i++)
{
closedge[i].adjvex = u;
closedge[i].lowcost = G.adj[i][u];
}
closedge[u].lowcost = 0;
//循环n次 每次找权值最小的边
for (j = 0; j < G.vexnum; j++)
{
min = INFINITY;
for (v = 0; v < G.vexnum; v++)
{
if ((closedge[v].lowcost != 0) && (closedge[v].lowcost < min))
{
min = closedge[v].lowcost;
k = v;
}
}
//在边表里加入权值最小的边
TE[j].v1 = closedge[k].adjvex;
TE[j].v2 = k;
TE[j].weight = closedge[k].lowcost;
//把权值最小的(刚添加到边表里的边)边的权值赋为0
closedge[k].lowcost = 0;
//修改closedge数组中的值
for (v = 0; v < G.vexnum; v++)
{
if ((closedge[v].lowcost != 0) && (G.adj[v][k] < closedge[v].lowcost))
{
closedge[v].lowcost = G.adj[v][k];
closedge[v].adjvex = k;
}
}
}
return TE;
}
再来一个输出邻接矩阵的方法
void OutPut(MGraph G)
{
int i;
int j;
for (i = 0; i < G.vexnum; i++)
{
printf("%c\t", G.vex[i].data);
}
printf("\n");
printf("------------------------------------\n");
for (i = 0; i < G.vexnum; i++)
{
for (j = 0; j < G.vexnum; j++)
{
printf("%d\t", G.adj[i][j]);
}
printf("\n");
}
}
main方法(这里的面方法只是测试用的,各位可根据自己的需要编写)
int main()
{
int j;
MGraph G;
MSTEdge *TE;
printf("1.create a graph\n2.insert a vertex\n3.insert an arc\n4.Output adjacency matrix\n5.minispantree\n");
while (1)
{
int a = 0;
printf("please enter the operation code:");
scanf("%d", &a);
switch (a)
{
case 1:
G = CreateGraph();
break;
case 2:
InsertVex(&G);
break;
case 3:
InsertArc(&G);
break;
case 4:
OutPut(G);
break;
case 5:
TE = PrimMiniSpanTree(G, 1);
printf("\tTE->v1\tTE->v2\tTE->weight\t\n");
for (j = 0; j < G.vexnum - 1; j++)
{
printf("%d\t", j);
printf("%d\t", TE[j].v1);
printf("%d\t", TE[j].v2);
printf("%d\t", TE[j].weight);
printf("\n");
}
break;
default:
printf("Opcode error!\n");
break;
}
}
return 0;
}
例:
运行结果:
插入节点:
插入边:
邻接矩阵:自定义100000为无穷(两个节点之间没有边)
最小生成树的边表: