一、目的
掌握图的常见应用算法的思想及其程序实现。
二、内容
1、内容
a) (1)键盘输入数据,分别建立一个有向图的邻接表和一个无向图的邻接矩阵。
b) (2)输出该邻接表和邻接矩阵。
c) (3)以有向图的邻接表为基础输出它的拓扑排序序列。
d) (4)以无向图的邻接矩阵为基础实现最小生成树的PRIM算法。
e) (5)以有向图的邻接矩阵为基础实现Dijkstra算法输出单源点到其它顶点的最短路径。
f) (6)在主函数中设计一个简单的菜单,分别调试上述算法。
g) (7)*综合训练:校园导航
h) 1)问题描述:
i) 在给出校园各主要建筑的名称信息及有路线连通的建筑之间的距离(或行进时间)的基础上,利用校园导航系统计算出给定的起点到终点之间距离最近(或行进时间最短)的行进路线。
j) 2)设计要求:文件读入或键盘方式读入校园主要建筑信息及建筑间的距离(或行进时间)信息。创建完地图后,以文件形式保存,以免重复创建。计算出给定的起点到终点之间距离最近(或行进时间最短)的行进路线,并输出该路线(包括经过哪些建筑)及其总距离(或总行进时间)。
2、主要数据类型与变量
1.类型定义
邻接表存储见07
邻接矩阵存储示例
#define MAX_VERTEX_NUM 20 //顶点最大个数
typedef enum {DG, DN, UDG, UDN} GraphKind;
typedef structArcCell{
VRType adj;
int weight; //边的权值
}ArcCell; AdjMatrix[MAX_VERTEX_NUM] [MAX_VERTEX_NUM];
typedef struct{
VertexTypevexs[MAX_VERTEX_NUM];
AdjMatrix arcs;
int vexnum, arcnum; //顶点的实际数,边的实际数
GraphKind kind;
}MGraph;
三、系统测试
1、测试方案
通过图形化界面,选择进入那个功能,然后开始一系列的测试,比如第一个的拓扑排序,通过输入顶点与边,然后实现拓扑排序。
2、测试结果
1.拓补排序
2.最小生成树
3.最短路径
附:程序源代码
DS.h
/*该头文件在每一章的程序中都要用到*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include "MGraph.h"
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFINITE 0xFFFFFFFF
typedef int Status;
SqStack.h
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef int SElemType;
typedef int Status;
typedef struct {
SElemType * base; //栈底指针
SElemType *top; //栈顶指针
Status stacksize; //栈可使用的最大容量
} SqStack;
Status InitStack (SqStack &S); //初始化空栈
Status GetTop(SqStack S, SElemType &e); //获得栈顶元素
Status Push(SqStack &S, SElemType e); //入栈操作
Status Pop(SqStack &S, SElemType &e); //出栈操作
Status StackEmpty(SqStack S); //判断栈是否为空
MGraph.h
#ifndef MGRAPH_H_INCLUDED
#define MGRAPH_H_INCLUDED
#define MAX 3000
#define N 10 /*最多顶点数*/
typedef char VertexType; /*顶点类型*/
typedef struct{
VertexType vexs[N]; /*存顶点的数组*/
int arcs[N][N];/*存边信息的矩阵*/
int vexnum, arcnum; /*顶点数,边数 */
}MGraph;
#endif // MGRAPH_H_INCLUDED
ALGraph.h
#include "SqStack.h"
#ifndef ALGRAPH_H_INCLUDED
#define ALGRAPH_H_INCLUDED
#define N 10 /*最多顶点数*/
typedef char VertexType; /*顶点类型*/
typedef int Status;
//定义边结点的结构
typedef struct ArcNode
{
Status adjvex; //边的邻接点的数据
Status weight; //对应边的权值
struct ArcNode *nextarc; //指向下一邻接点的指针
}ArcNode;
//定义定点结构
typedef struct VNode
{
VertexType data; //顶点数据
ArcNode *firstarc; //指向该顶点第一条弧的指针*/
}VNode,AdjList[N];
typedef struct
{
AdjList vertices;
Status vexnum, arcnum; //图的顶点数和弧数
}ALGraph;
#endif // ALGRAPH_H_INCLUDED
SqStack.cpp
/*关于栈的基本操作定义*/
#include "DS.h"
#include "SqStack.h"
/*初始化一个空栈*/
Status InitStack (SqStack &S){
S.base = (SElemType *)malloc(STACK_INIT_SIZE *sizeof(SElemType));
if (! S.base) exit (OVERFLOW);
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return OK;
}
/*判断栈是否为空*/
Status StackEmpty(SqStack S)
{
if(S.top == S.base)
return OK;
else
return ERROR;
}
/*取栈顶元素*/
Status GetTop(SqStack S, SElemType &e){
if (S.top == S.base) return ERROR;
e= * (S.top-1);
return OK;
}
/*入栈*/
Status Push(SqStack &S, SElemType e){
if (S.top - S.base>=S.stacksize){
S.base=(SElemType*)realloc(S.base,
(S.stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!S.base) exit(OVERFLOW);
S.top=S.base+S.stacksize;
S.stacksize+=STACKINCREMENT;
}
*S.top++ = e;
return OK;
}
/*出栈*/
Status Pop(SqStack &S, SElemType &e){
if( S.top == S.base) returnERROR;
e = *--S.top;
return OK;
}
Main.cpp
#include "DS.h"
#include "ALGraph.h"
/*查找顶点v在邻接表存储法中的下标*/
int LocateVex1(ALGraph G, char v)
{
int i;
for(i = 0; i < G.vexnum;i++)
if(G.vertices[i].data == v)
return i; /*找到顶点返回下标*/
return -1;
}
/*在邻接表中插入顶点v的邻接点w*/
int InsertList1(ALGraph &G, int i, int j)
{
ArcNode *p;
if(i==-1||j==-1||i==j)
return ERROR;
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j; /*插入邻接点*/
p->nextarc=NULL;
if(!G.vertices[i].firstarc)
G.vertices[i].firstarc=p; /*头插法*/
else
{
p->nextarc=G.vertices[i].firstarc->nextarc;
G.vertices[i].firstarc->nextarc=p;
}
return 1;
}
/*请写出有向带权图的邻接表的生成函数*/
int CreateDN1(ALGraph &G)
{
int i, j, k,w;
char v1, v2;
printf("请输入有向图G的顶点数和边数:");
scanf("%d %d",&G.vexnum, &G.arcnum);
printf("请输入%d个顶点名字(以空格区分):", G.vexnum);
for(i = 0; i < G.vexnum; i++)
{
scanf(" %c",&G.vertices[i].data); /*请注意输入格式*/
G.vertices[i].firstarc =NULL; /*邻接表初始化为空*/
}
for(k = 1; k <= G.arcnum;k++)
{
printf("请输入第%d条边的两个顶点(以字符表示,字符间以空格区分)和权值:", k);
scanf(" %c %c%d", &v1, &v2,&w); /*请注意输入格式*/
i = LocateVex1(G, v1); /*确定v1和v2的下标*/
j = LocateVex1(G, v2);
InsertList1(G, i, j); /*生成各自的邻接表(记录权值)*/
}
return OK;
}
/*查找顶点v在邻接矩阵存储法中的下标*/
int LocateVex(MGraph G, char v)
{
int i;
for(i = 0; i < G.vexnum;i++)
if(G.vexs[i] == v)
return i;
return -1;
}
/*请写出有向带权图的邻接矩阵的生成函数*/
Status CreateDN(MGraph &G)
{
int i, j, k, value;
char v1, v2;
printf("请输入图G的顶点数和边数:");
scanf("%d %d",&G.vexnum, &G.arcnum);
printf("请输入%d个顶点名字(以空格区分):", G.vexnum);
for(i = 0; i < G.vexnum;i++)
scanf(" %c",&G.vexs[i]); /*请注意输入格式*/
for(i = 0; i < G.vexnum;i++)
for(j = 0; j < G.vexnum;j++)
G.arcs[i][j] = MAX;
for(k = 1; k <= G.arcnum;k++)
{
printf("请输入第%d条边的两个顶点(以字符表示,字符间以空格区分)及权值:", k);
scanf(" %c %c%d", &v1, &v2, &value); /*请注意输入格式*/
i = LocateVex(G, v1); /*确定v1和v2的下标*/
j = LocateVex(G, v2);
if(i >= 0 && j>= 0) /*修改邻接矩阵的信息*/
G.arcs[i][j] = value;
}
return OK;
}
/*在邻接表中插入顶点v的邻接点w*/
int InsertList(ALGraph &G, int i, int j)
{
ArcNode *p;
if(i==-1||j==-1||i==j)
return ERROR;
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j; /*插入邻接点*/
p->nextarc=NULL;
if(!G.vertices[i].firstarc)
G.vertices[i].firstarc=p; /*头插法*/
else
{
p->nextarc=G.vertices[i].firstarc->nextarc;
G.vertices[i].firstarc->nextarc=p;
}
return 1;
}
/*键盘输入数据,分别建立一个有向图的邻接表和一个无向图的邻接矩阵。
(2)输出该邻接表和邻接矩阵。
(3)以有向图的邻接表为基础输出它的拓扑排序序列。
(4)以无向图的邻接矩阵为基础实现最小生成树的PRIM算法。
(5)以有向图的邻接矩阵为基础实现Dijkstra算法输出单源点到其它顶点的最短路径。
(6)在主函数中设计一个简单的菜单,分别调试上述算法。*/
//生成入度
void FindIndegree(ALGraph G,int *arr)
{
ArcNode *p;
for(int i=0; i<G.vexnum;i++)
{
p=G.vertices[i].firstarc;
while(p)
{
intindex=p->adjvex;//得到这个节点所指向的节点的下标
arr[index]++;
p=p->nextarc;
}
}
}
int TopoSort(ALGraph G)
{
int indegree[30]= {0};
SqStack S;
FindIndegree(G,indegree);//生成各顶点的入度
InitStack(S);
for(int i=0; i<G.vexnum;++i)
{
if(!indegree[i])
Push(S,i);
}
int count=0;
while(!StackEmpty(S))
{
ArcNode *p;
int i;
Pop(S,i);
printf("%c",G.vertices[i].data);
count++;
p=G.vertices[i].firstarc;
while(p)
{
int k;
k=p->adjvex;
if(!(--indegree[k]))
{
Push(S,k);
}
p=p->nextarc;
}
}
if(count<G.vexnum)//用来判断回路
return -1;
else
return 1;
}
//ar VertexType arcs 矩阵 ,vexnum定点数,arcnum 边数
struct node
{
VertexType adjvex;
int lowcost;
} closedge[30]; //用来储存已经加入到树中的节点
/*最小生成树思想:每次从已有的顶点的周围的边中选取权值最小的边
加入到图中,将另外的点加入到这个顶点集中,然后循环这个过程*/
int minimum(MGraph G,struct node * closedge)//返回最小代价边
{
int min=3000;
int index=0;
//需要遍历已经加入到顶点集的元素的那一行,来寻找权值最小的边
for(int i=0; i<G.vexnum;i++)
{
if(closedge[i].lowcost==0)
{
for(int j=0;j<G.vexnum; j++)
{
if(closedge[j].lowcost==0)
continue;
else if(G.arcs[i][j] < min)
{
min=G.arcs[i][j];
index=j;
}
}
}
}
for(int i=0; i<G.vexnum;i++)
{
if(closedge[i].lowcost<min && closedge[i].lowcost!=0)
{
min=closedge[i].lowcost;
index=i;
}
}
return index;
}
void CreateTree(MGraph G,VertexType u)
{
//决定生成树从哪开始生成
int k=LocateVex(G,u);
//初始化辅助数组
for(int j=0; j<G.vexnum;++j)
{
if(j!=k)
{
closedge[j].adjvex=u;
closedge[j].lowcost=G.arcs[k][j];
}
}
closedge[k].lowcost=0;
for(int i=1; i<G.vexnum;++i)
{
k=minimum(G,closedge);//k现在是要加入到顶点集的下一个顶点在数组中对应的下标
printf("%c --- %c",closedge[k].adjvex,G.vexs[k]);
closedge[k].lowcost=0;//将第k个顶点加入到数组中
for(int j=0; j<G.vexnum;++j)
{
if(G.arcs[k][j]<closedge[j].lowcost)
{
closedge[j].adjvex=G.vexs[k];
closedge[j].lowcost=G.arcs[k][j];
}
}
}
}
//最短路径问题
void Dijkstra(MGraph G,int v0,int *dist,int *path)
{
int s[N];
int minDis,u;
for(int i=0; i<G.vexnum;i++)
{
s[i]=0;
dist[i]=G.arcs[v0][i];
if(dist[i]<INT_MAX)
path[i]=v0;
else
path[i]=-1;
}
s[v0]=1;
dist[v0]=0;
for(int i=1; i<G.vexnum;i++)
{
minDis=INT_MAX;
for(int j=1;j<=G.vexnum; j++)
{
if(dist[j]<minDis&& !s[j])
{
minDis=dist[j];
u=j;
}
if(minDis<INT_MAX)
s[u]=1;
}
for(int j=0; j<G.vexnum;j++)
{
if(!s[j]&&G.arcs[u][j]<INT_MAX)
if(dist[j]>dist[u]+G.arcs[u][j])
{
dist[j]=minDis+G.arcs[u][j];
path[j]=u;
}
}
}
for(int i=0; i<G.vexnum;i++)
{
printf("%d",dist[i]);
}
}
void menu()
{
printf("\t\t\t 单链表基本操作\n\n");
printf("\t\t\t1.拓扑排序\n");
printf("\t\t\t2.最小生成树\n");
printf("\t\t\t3.最短路径\n");
printf("\t\t\t0.退 出\n\n");
}
int main()
{
int choice;
menu();
printf("请输入你的操作:");
scanf("%d",&choice);
while(1){
if(choice==1)
{
ALGraph G;
//拓扑排序
CreateDN1(G);
printf("拓扑排序为:\n");
TopoSort(G);
break;
}
else if(choice==2)
{
MGraph G;
printf("请建立有向图:\n");
CreateDN(G);
char ch;
printf("请确认最小生成树的定点元素:\n");
scanf("%c",&ch);
CreateTree(G,ch);
break;
}
else if(choice==3)
{
//最短路径问题
MGraph G;
printf("请建立有向图:\n");
CreateDN(G);
int dist[G.vexnum];//用来储存每个节点到起始点的最短路径
int path[G.vexnum];
//为顶点i的最优前驱节点
printf("请问从哪个点开始遍历:\n");
char ch;
scanf("%c",&ch);
int v0=LocateVex(G,ch);
Dijkstra(G,v0,dist,path);
break;
}
else
{
break;
}
}
return 0;
}