[问题描述]
若要在n个城市间建立通信联络网,只需要n-1条线路即可。如何以最低的经济代价建设这个通信网,是一个网的最小生成树问题。
[基本要求]
(1)利用图雷姆或克鲁斯卡尔算法求网的最小生成树;
(2)以文本形式输出生成树的各条边及其权值。
[实现提示]
(1)通信线路一旦建立起来一定是双向的,因此构造最小生成树的网一定是无向网;
(2)n个城市间,最多可设置n(n-1)/2条线路;n个城市间建立通信网,只需n-1条线路。问题转化为:如何在可能的线路中选择n-1条,能把所有城市(顶点)均连起来,且总耗费(各边权值之和)最小。
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define INFINITY 99999
#define MAX_VERTEX_NUM 20 //最大顶点个数
typedef int Status;
typedef int InfoType;
typedef char VertexType;
//弧段结点结构
typedef struct ArcNode
{
int adjvex; //该弧段所指向的顶点的位置
struct ArcNode *nextArc; //指向下一条弧段的指针
InfoType info; //该弧段相关信息
} ArcNode;
//顶点表结构
typedef struct VNode
{
VertexType data; //图结点的数据类型
ArcNode *firstarc; //图结点的头指针,指向依附于该顶点的第一条弧段
}VNode, AdjList[MAX_VERTEX_NUM]; //定义了顶点结点和顶点表类型
//图结构
typedef struct
{
AdjList vertices; //图的顶点表
int vexnum, arcnum; //图的顶点数和弧数
int kind; //图的种类标志
}ALGraph;
//边信息-用于最小生成树算法存储边信息
typedef struct
{
int startnode;
int endnode;
InfoType weight;
}Edge;
//用于Prim最小生成树算法
typedef struct
{
VertexType adjvex;
InfoType lowcost;
} Closedge[MAX_VERTEX_NUM];
Closedge closedge;
Status PrintElement(ALGraph G, int e) { // 输出元素e的值
printf("%d ", G.vertices[e].data); // 实用时,加上格式串
return OK;
}
//创建图
Status CreateGraph(ALGraph &G);
//销毁图
Status DestroyGraph(ALGraph &G);
//获取顶点位置
int LocateVex(ALGraph G, VertexType u);
//获取v的第一个邻接顶点
int FirstAdjVex(ALGraph G, VertexType v);
//获取返回v的相对于w的下一个邻接顶点
int NextAdjVex(ALGraph G, VertexType v, VertexType w);
//添加新顶点v
Status InsertVex(ALGraph &G, VertexType v);
//删除顶点v
Status DeleteVex(ALGraph &G, VertexType v);
//添加弧段<v,w>
Status InsertArc(ALGraph &G, VertexType v, VertexType w);
//删除弧段<v,w>
Status DeleteArc(ALGraph &G, VertexType v, VertexType w);
Status CreateUDN(ALGraph &G)
{
int v1, v2, w;
int i, j, k;
ArcNode *node = NULL;
printf("请输入图G的顶点数,边数(空格隔开):\n");
scanf("%d%d", &G.vexnum, &G.arcnum);
printf("请输入顶点(整型数,空格隔开):\n");
for (i = 0; i<G.vexnum; i++)
{
scanf("%d", &G.vertices[i].data);
G.vertices[i].firstarc = NULL;
}
for (k = 0; k<G.arcnum; k++)
{
printf("请输入%d条弧信息:v1,v2,w(空格隔开)\n", k+1);
scanf("%d%d%d", &v1, &v2, &w);
i = LocateVex(G, v1);
j = LocateVex(G, v2);
node = (ArcNode*)malloc(sizeof(ArcNode));
node->adjvex = j;
node->info = w;
//头插入法
node->nextArc = G.vertices[i].firstarc;
G.vertices[i].firstarc = node;
}
return OK;
}//CreateUDN-无向网
//销毁图
Status DestroyGraph(ALGraph &G)
{
return OK;
}
//获取顶点位置
int LocateVex(ALGraph G, VertexType u)
{
int i;
for (i = 0; i<G.vexnum; i++)
{
if (G.vertices[i].data == u)
return i;
}
return -1;
}
//获取v的第一个邻接顶点
int FirstAdjVex(ALGraph G, VertexType v)
{
ArcNode *p;
int i;
i = LocateVex(G, v);
p = G.vertices[i].firstarc;
if (p)
return p->adjvex;
return -1;
}
//获取返回v的相对于w的下一个邻接顶点
int NextAdjVex(ALGraph G, VertexType v, VertexType w)
{
ArcNode *p;
int i;
i = LocateVex(G, v);
p = G.vertices[i].firstarc;
while (p)
{
if (G.vertices[p->adjvex].data == w)
{
p = p->nextArc;
if (p) return p->adjvex;
else return -1;
}
p = p->nextArc;
}
return -1;
}
void PrintGraph(ALGraph G)
{
int i;
ArcNode *p = NULL;
printf("顶点1(弧尾) 顶点2(弧头) 该该边信息:\n");
for (i = 0; i<G.vexnum; i++)
{
printf("%d", G.vertices[i].data);
p = G.vertices[i].firstarc;
while (p)
{
printf("--%d %d ", (p->adjvex) + 1, p->info);
p = p->nextArc;
}
printf("\n");
}
}
int minimum(ALGraph G, Closedge elosedge)
{
int i, j;
int min = INFINITY;
for (i = 0; i<G.vexnum; i++)
{
if (elosedge[i].lowcost>0 && min>elosedge[i].lowcost)
{
min = elosedge[i].lowcost;
j = i;
}
}
return j;
}
//最小生成树——Prim算法
void MiniSpanTree_PRIM(ALGraph G, VertexType u)
{ // 算法7.9
// 用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边。
// 记录从顶点集U到V-U的代价最小的边的辅助数组定义:
int i, j, k;
ArcNode * p;
k = LocateVex(G, u);
for (j = 0; j<G.vexnum; ++j)
{ // 辅助数组初始化
if (j != k)
{
closedge[j].adjvex = u;
closedge[j].lowcost = INFINITY;
}
}
//右边的距离值
p = G.vertices[k].firstarc;
while (p)
{
closedge[p->adjvex].lowcost = p->info;
p = p->nextArc;
}
closedge[k].lowcost = 0; // 初始,U={u}
for (i = 1; i<G.vexnum; ++i)
{ // 选择其余G.vexnum-1个顶点
k = minimum(G, closedge);
// 求出T的下一个结点:第k顶点
// 此时closedge[k].lowcost =
// MIN{ closedge[vi].lowcost | closedge[vi].lowcost>0, vi∈V-U }
printf("(%d,%d,%d)\n", closedge[k].adjvex, G.vertices[k].data, closedge[k].lowcost); // 输出生成树的边
closedge[k].lowcost = 0; // 第k顶点并入U集
p = G.vertices[k].firstarc;
while (p)
{
if (p->info>0 && p->info < closedge[p->adjvex].lowcost)
{
// 新顶点并入U后重新选择最小边
// closedge[j] = { G.vexs[k], G.arcs[k][j].adj };
closedge[p->adjvex].adjvex = G.vertices[k].data;
closedge[p->adjvex].lowcost = p->info;
}
p = p->nextArc;
}
}
} // MiniSpanTree
//判断边数组中是否有端点为i,j的边
Status HasEdge(Edge *edges, int n, int i, int j)
{
int k;
for (k = 0; k<n; k++)
{
if ((edges[k].endnode == i&&edges[k].startnode == j) || (edges[k].startnode == i&&edges[k].endnode == j))
return TRUE;
}
return FALSE;
}
void main()
{
//创建图
ALGraph G;
int a, b;
CreateUDN(G);
printf("\n创建图成功!\n"); PrintGraph(G);
printf("最小生成树Prim算法:\n");
printf("输入从哪个顶点开始构建最小生成树:\n");
scanf("%d", &b);
MiniSpanTree_PRIM(G, b);
printf("\n");
}
测试使用的图:
顶点数:6 边数:10
顶点:1,2,3,4,5,6