(1)、实验题目:给定一个地区的n 个城市间的距离网,用Prim算法或Kruskal算法建立最小生成树,并得到的最小生成树的代价。
1、城市间的距离网采用的邻接矩阵表示,邻接矩阵的存储结构定义采用课本中给出的定义,若两个城市之间不存在道路,则将相应边的权值设为自己定义的无穷大值。要求在屏幕上显示得到的最小生成树中包括了哪些城市间的道路,并显示得到的最小生成树的代价。
2、表示城市间距离网的邻接矩阵(要求至少6个城市,10条边)
3、最小生成树中包括的边及其权值,并显示得到的最小生成树的代价。
1、根据题目要求,首先要建立城市间的距离网,并且采用邻接矩阵表示,则使用到的数据结构是图的形式。图的作用是实现城市的距离网,采用顺序存储结构。数据结构可以用如下表示:
typedef struct
{
SeqList Vertices;
int edge[MaxVertices][MaxVertices];
int num_edge;
}Graph;
2、完成距离网的存储后,就要用Prim算法和Kruskal算法来实验最小生成树。
Prim算法是以结点为最小生成树的开始,再找其相应的最小边来实现。可以设计为两个参数,一个参数为图G,另一个参数是通过函数得到的最小生成树结点数据和相应结点的边的权值数据closeVertex.所以其数据结构可以定义如下:
typedef struct
{
DataType vertex;
int weight;
}TreeNode;
Kruskal算法是以图中最小边开始慢慢寻找最小生成树的代价。所以它的结构包含最小边及边的两结点的下标,标置位是判断结点是否已经参加比较。其结构可以定义如下:
typedef struct
{
int row;
int col;
int weight;
int flag;
}TreeEdge;
1、首先设计的是顺序表,用来存放图中的各个结点的信息。这样可以看到结点的多少,又可以知道每个结点对应的下标,便于操作。
2、图的操作:图的操作主要体现在要实现城市的距离网的信息,所以其操作有:初始化,插入结点、插入边。
A、
初始化GraphInitiate(G,n);初始化图G,n个结点个数。
B、
插入结点InsertVertex(G,vertex);在图中插入结点vertex.
C、
插入结点InsertEdge(G,v1,v2,weight);在图中插入边<v1,v2>,边<v1,v2>的权值为weight.
3、prim算法的设计:Prim算法的函数设计为void Prim(Graph G);
A、
函数中首先定义一个数组lowCost,数组过犹不及lowCost[v]中保存了集合U中结点u(i)与集合VU中结点v(j)的所有边中当前具有最小权值的边<u,v>.
B、
集合U的初值为U={序号为0的结点}。lowCost的初始值为邻接矩阵数组中第0行的值,这样初始时lowCost中就存放了从集合U中结点0到集合VU中各个结点的权值。
C、
每次从lowCost中寻找具有最小权值的边,根据lowCost的定义,这样的边其弧头结点必然为集合U中的结点,其弧尾结点必然为集合VU中的结点为。当选到一条这样的边(u,v)时,就保存其结点数据和权值的数据到参数closeVertex中,并将lowCost[v]置-1。
D、
当结点v从集合VU加入到集合U后,若存在一条边<u,v>,u是集合U的结点,v是集合VU的结点,且边<u,v>较原先lowCost[v]的权值更小,则用这样的权值修改原先lowCost[v]中的相应权值。
3、Kruskal算法的设计:Kruskal算法函数设计为void Kruskal(Graph G).
A、
首先将所有有权值(MaxSize>weight>0)的边存放到lowCost数组里。并且标置位flag置0。
B、
根据lowCost存放所有的结点来寻找最小的权值。若找到刚先保存起来,标置位为1。并要判断是否符合。
C、
将B中找到的边的两个结点的下表与顺序表中的数据比较,如果没有则进表保存,如果两个都有则说明此边会与前面找到的最小边构成了回路,不符合。
首先要建立顺序表其函数包含在头文件:SeqList.h中
#define MaxSize 20
typedef struct
{
DataType list[MaxSize];
int size;
}SeqList;
void ListInitiate(SeqList *L)
{
L->size=0;
}
int ListLength(SeqList L)
{
return L.size;
}
int ListInsert(SeqList *L,int i,DataType x)
{
int j;
if(i<0||i>MaxSize)
{
printf("插入参数不合法!");
return 0;
}
else
{
for(j=L->size;j>i;j--)
L->list[j]=L->list[j-1];
L->list[i]=x;
L->size++;
return 1;
}
}
int ListDelete(SeqList *L,int i,DataType *x)
{
int j;
if(i<0||i>MaxSize)
{
printf("插入参数不合法!");
return 0;
}
else
{
*x=L->list[i];
for(j=i;j<L->size;j++)
L->list[j]=L->list[j+1];
L->size--;
return 1;
}
}
void ListGet(SeqList *L,int i,DataType *x)
{
*x=L->list[i];
}
int Search(SeqList *L,DataType x,int *i)
{
int j;
for(j=0;j<L->size;j++)
{
if(x==L->list[j])
{
*i=j;
return 1;
}
}
printf("无该结点");
return 0;
}
int IsIn(SeqList L,DataType x)
{
int i;
for(i=0;i<L.size;i++)
{
if(x==L.list[i])
return 1;
}
return 0;
}
#include "SeqList.h"
#define MaxWeight 10000
typedef struct
{
SeqList Vertices;
int edge[MaxVertices][MaxVertices];
int num_edge;
}Graph;
void GraphInitiate(Graph *G,int n)
{
int i,j;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
if(i==j) G->edge[i][j]=0;
else
G->edge[i][j]=MaxWeight;
}
G->num_edge=0;
ListInitiate(&G->Vertices);
}
void VertexInsert(Graph *G,DataType vertex)
{
ListInsert(&G->Vertices,G->Vertices.size,vertex);
}
void EdgeInsert(Graph *G,DataType x1,DataType x2,int weight)
{
int v1,v2;
Search(&G->Vertices,x1,&v1);
Search(&G->Vertices,x2,&v2);
G->edge[v1][v2]=weight;
G->edge[v2][v1]=weight;
G->num_edge++;
}
typedef struct
{
DataType vertex;
int weight;
}TreeNode;
void prim(Graph G)
{
DataType x;
int m=0,n=G.Vertices.size,minCost;
TreeNode *closevertex;
int *lowCost=(int *)malloc(sizeof(int)*n);
int i,j,k;
closevertex=(TreeNode *)malloc(sizeof(TreeNode)*n);
for(i=1;i<n;i++)
lowCost[i]=G.edge[0][i];
ListGet(&G.Vertices,0,&x);
closevertex[0].vertex=x;
lowCost[0]=-1;
for(i=1;i<n;i++)
{
minCost=MaxWeight;
for(j=1;j<n;j++)
{
if(lowCost[j]<minCost&&lowCost[j]>0)
{
minCost=lowCost[j];
k=j;
}
}
ListGet(&G.Vertices,k,&x);
closevertex[i].vertex=x;
closevertex[i].weight=minCost;
lowCost[k]=-1;
for(j=1;j<n;j++)
{
if(G.edge[k][j]<lowCost[j])
lowCost[j]=G.edge[k][j];
}
}
printf("初始结点=%c ",closevertex[0].vertex);
for(i=1;i<n;i++)
{
printf("结点=%c 边的权值=%d ",closevertex[i].vertex,closevertex[i].weight);
m=m+closevertex[i].weight;
}
printf("代价和为:%d",m);
}
typedef struct
{
int row;
int col;
int weight;
int flag;
}TreeEdge;
void kruskal(Graph *G)
{
typedef int DataType;
int daijia=0;
int n=G->num_edge;
int minCost;
TreeEdge treeedge[20];
TreeEdge *lowCost=(TreeEdge *)malloc(sizeof(TreeEdge)*n);
int i,j,k,m;
SeqList L;
ListInitiate(&L);
i=0;
while(i<n)
{
for(j=0;j<G->Vertices.size;j++)
for(k=j;k<G->Vertices.size;k++)
{
if(G->edge[j][k]>0&&G->edge[j][k]<MaxWeight)
{
lowCost[i].weight=G->edge[j][k];
lowCost[i].row=j;
lowCost[i].col=k;
lowCost[i].flag=0;
i++;
}
}
}
m=0;
while(m<G->Vertices.size-1)
{
minCost=MaxWeight;
for(j=0;j<n;j++)
{
if(lowCost[j].weight<minCost&&lowCost[j].flag==0)
{
minCost=lowCost[j].weight;
k=j;
}
}
lowCost[k].flag=1;
if(!(IsIn(L,lowCost[k].row)&&IsIn(L,lowCost[k].col)))
{
if(!IsIn(L,lowCost[k].row))
ListInsert(&L,L.size,lowCost[k].row);
if(!IsIn(L,lowCost[k].col))
ListInsert(&L,L.size,lowCost[k].col);
treeedge[m]=lowCost[k];
m++;
}
}
for(i=0;i<m;i++)
{printf("权值=%d 两城市=%c %c ",treeedge[i].weight,G->Vertices.list[treeedge[i].row],G->Vertices.list[treeedge[i].col]);
daijia+=treeedge[i].weight;
}
printf("代价和为:%d",daijia);
}
#include <stdio.h>
#include <stdlib.h>
typedef char DataType;
#define MaxVertices 20
#include "Vertex.h"
#include "prim.h"
#define MaxNums 200
#include<string.h>
void f(Graph G)
{
printf("…………prim算法得到的结果为: ");
prim(G);
}
int main()
{
Graph G;
DataType c[100];
DataType c1[100];DataType c2[100];
int number;
FILE *fp ;
int i1,j1;
int nums=0;
DataType words[100][21];
fp=fopen("data.txt", "r");
if ( fp==NULL )
{
printf("open file error ");
return -1;
}
for( i1=0;i1<100;i1++ )
{
if ( fscanf(fp, "%20s", words[i1] )!= 1 )
break;
}
fclose(fp);
for( j1=0;j1<i1;j1++ )
{
nums++;
}
printf("下面是从文件读取到的数据:");
printf(" ");
int n = (atoi(words[0]));
number = n;
printf("城市个数:%d",n);
printf(" ");
GraphInitiate(&G,n);
for(int i=0;i<n;i++)
{
c[i] = *words[i+1];
printf( "城市名称:%s ", words[i+1] ) ;
printf(" ");
VertexInsert(&G,c[i]);
}
int m = (atoi(words[n+1]));
printf( "边数:%s ", words[n+1] ) ;
printf(" ");
for(int j=0;j<m;j++)
{
c1[j] = *words[number+2+3*j];
c2[j] = *words[number+3+3*j];
int q = (atoi(words[number+4+3*j]));
EdgeInsert(&G,c1[j],c2[j],q);
EdgeInsert(&G,c1[j],c2[j],q);
}
printf(" ");
printf("矩阵:");
printf(" ");
for(int i=0;i<G.Vertices.size;i++)
{
for(int j=0;j<G.Vertices.size;j++)
printf("%7d",G.edge[i][j]);
printf(" ");
}
printf(" ");
f(G);
return 0;
}
请输入城市个数:7
请依次输入城市名:a b c d e f g
请输入要输入的边数:10
请输入两城市的权值:
输入城市:a 与它相连的城市b 它们的权值:50
输入城市:a 与它相连的城市c 它们的权值:60
输入城市:b 与它相连的城市d 它们的权值:65
输入城市:b 与它相连的城市e 它们的权值:40
输入城市:c 与它相连的城市d 它们的权值: 52
输入城市:c 与它相连的城市g 它们的权值:45
输入城市:d 与它相连的城市e 它们的权值:50
输入城市:d 与它相连的城市f 它们的权值:30
输入城市:d 与它相连的城市g 它们的权值:42
输入城市:e 与它相连的城市f 它们的权值:70
0 50 60 10000 10000 10000 10000
50 0 10000 65 40 10000 10000
60 10000 0 52 10000 10000 45
10000 65 52 0 50 30 42
10000 40 10000 50 0 70 10000
10000 10000 10000 30 70 0 10000
10000 10000 45 42 10000 10000 0
…………prim算法得到的结果为:
初始结点=a
结点=b 边的权值=50
结点=e 边的权值=40
结点=d 边的权值=50
结点=f 边的权值=30
结点=f 边的权值=42
结点=c 边的权值=45
代价和为:257 …………
…………kruskal算法得到的结果为:
权值=30 两城市=d f
权值=40 两城市=b e
权值=42 两城市=d g
权值=45 两城市=c g
权值=50 两城市=a b
权值=50 两城市=d e
代价和为:257…………
附:增加模块:在原来距离网的基础上重新再加一个节点并重新设计最小生成树。
(1)
首先先把原来的图中信息保存,再重新初始化一个n+1个结点的图。
void Insert(Graph *g)
{
DataType m,n;
int k;
int i,j;
char c='y',c1;
printf("请输入要插入的结点:");
scanf("%s",&m);
VertexInsert(g,m);
while(c=='y'||c=='Y')
{
printf("请输入它与哪个结点相连:");
scanf("%s",&n);
printf("请输入它们的权值:");
scanf("%d",&k);
EdgeInsert(g,m,n,k);
printf("它是否还有其它相连的结点(y/n):");
scanf("%s",&c1);
c=c1;
}
for(i=0;i<g->Vertices.size;i++)
{
for(j=0;j<g->Vertices.size;j++)
printf("%7d",g->edge[i][j]);
printf(" ");
}
}