图的建立和应用.docx
《图的建立和应用.docx》由会员分享,可在线阅读,更多相关《图的建立和应用.docx(65页珍藏版)》请在冰豆网上搜索。
![图的建立和应用.docx](https://file1.bdocx.com/fileroot1/2023-8/19/da885809-ca2c-433c-bf86-a8e7d8d7944d/da885809-ca2c-433c-bf86-a8e7d8d7944d1.gif)
图的建立和应用
一、实验目的
熟悉图的存储方式,实现图的邻接矩阵或者邻接表的存储方式下的基本运算,特别是深度遍历和广度遍历;掌握以图为基础的一些常用算法,如最小生成树、拓扑排序、最短路径等
二、需求分析
1、定义图的邻接表存储结构。
2、在定义的存储结构下实现下面的操作:
(1)图的创建操作;
(2)图的销毁操作;
(3)图的深度遍历的递归算法;
(4)图的广度遍历算法;
(5)求图的最小生成树算法;
(6)求图的最短路径算法。
3、图的深度遍历算法的非递归算法;应用图的拓扑排序求关键路径的算法(选做)。
#include"stdlib.h"
#include"stdio.h"
#defineEdgeTypeint
#defineVertexTypechar
#defineMaxVerNum10
#defineMAXSIZE20
#defineDataTypeint
#defineMAX1000
#definetrue1
#definefalse0
intvisited[MaxVerNum];
//定义的一个标志图的顶点是否已经被访问的全局数组
inttag;
//用于标识图的边是否具有权值,有则tag=true,否则为false
typedefstructenode{
intadjvertex;
EdgeTypeinfo;
structenode*next;
}EdgeNode;
//定义边的信息
typedefstructvnode{
VertexTypevertex;
EdgeNode*fristEdge;
}VertexNode;
//顶点的信息
typedefstructtnode{
charvertex1,vertex2;
EdgeTypeinfo;
structtnode*next;
}*PlowestTree,lowestTree;
//prim需要的最小生成树边的数据结构
typedefstruct{
intver1,ver2;
EdgeTypeinfo;
}edgeInfo;
//kruskal法需要的数据结构
typedefstruct{
VertexNodeadjlist[MaxVerNum];
intn,e;
}ALGraph;
//定义邻接表存储的图
typedefstruct{
intdata[MAXSIZE];
intfront,rear;
}sepQueue,*PsepQueue;
//队的数据结构
typedefstruct{
DataTypedata[MAXSIZE];
inttop;
}sepStack,*PsepStack;
//栈的数据结构
typedefstructnode{
intdata;
structnode*next;
}Linkstack,*PLinkstack;
typedefstruct{
PLinkstacktop;
}stack,*Pstack;
//链栈,用于求解拓扑序列
PstackInit_stack();
/*函数功能:
初始化一个栈*/
intempty_stack(Pstacks);
//判断一个栈是否为空
intpush_stack(Pstacks,intdata);
//入栈
intpop_stack(Pstacks,int*data);
//出栈
voidDestory_stack(Pstack*s);
//销毁栈
PsepStackInit_sepStack();
//初始化一个栈
intpush_sepStack(PsepStackp,DataTypedata);
//入栈,返回0表示入栈失败,返回1表示入栈成功
intpop_sepStack(PsepStackp,DataType*data);
//出栈,返回0表示出栈失败,返回1表示出栈成功
intempty_sepStack(PsepStackp);
//判断栈是否为空,返回0表示栈空,返回1表示出栈非空
voidDestory_sepStack(PsepStack*p);
//销毁栈
PsepQueueInit_sepQueue();
//队的初始化
intempty_sepQueue(PsepQueueQ);
//判断队是否为空
intpush_sepQueue(PsepQueueQ,intdata);
//新队员入队
intpop_sepQueue(PsepQueueQ,int*data);
//队员出队
voidDestory_sepQueue(PsepQueue*Q);
//销毁队列
ALGraphcreate_undALGraph();
//创建一个无向图
voidprintf_ALGraph(ALGraphALG);
//输出采用邻接表存储的图
voiddepth_Graph(ALGraphALG);
//深度遍历图(可以计算图有几个连通分量)
voidDFS(ALGraphALG,intv);
//深度遍历图(为depth_Graph的子函数)
voidbread_Graph(ALGraphALG);
//广度遍历图(可以计算图有几个连通分量)
voidBFS(ALGraphALG,intv);
//广度遍历图(为bread_Graph的子函数)
voidcatch_values(ALGraph*ALG);
//为图中的边带权
voiduncatch_values(ALGraph*ALG);
//不为图中的边带权,即创建图时,边没有带权
voidunDepth_Graph(ALGraphALG);
//非递归法深度遍历图
voidselect_min(intlist[],int*min,intsize);
//从指定的数组中选择最小值的数组下标
PlowestTreesearch_prim_lowestTree(ALGraphALG);
//找无向带权图的最小生成树
voidprintf_lowTree(PlowestTreeTree);
//输出最小生成树的具体边组成
voidsort_edge(edgeInfoedges[],intsize);
//对边集进行按权值的升序排序
PlowestTreesearch_kruskal_lowcost(ALGraphALG);
//kruskal法寻找最小生成树
voiduncatch_Dvalues(ALGraph*DG);
//创建的有向图边不带权值
voidcatch_Dvalues(ALGraph*DG);
//创建的有向图边带有权值
ALGraphcreate_DGraph();
//创建一个有向图,函数中会询问是否给边带权值
voidselect_min(intarray[],int*min,intsize);
//从数组中选择一个最小的值,min用于存该值的下标
voidshortest_path(ALGraphG);
//求最小路径
voidprintf_vertex_shortPath(intshortPath[],ALGraphG);
//输出0到其余点的最短距离
voidcaculate_indegree(intinSum[],ALGraphDG);
//计算所有顶点的入度
inttopoOrder(ALGraphDG,inttopo[]);
//求有向图的拓扑序列
voidprintf_topo_order(ALGraphG,inttopo[]);
//输出拓扑序列
ALGraphcreate_opposite_ALGraph(ALGraphG);
//为一个邻接表图创建逆邻接表图
voidDestory_ALGraph(ALGraph*G);
//销毁一个邻接表存储的图
voidprintf_opposite_Graph(ALGraphG);
//输出逆邻接表表示的图
voidCritical_path(ALGraphG);
//AOE网的关键路径
voidprintf_event_early_later(ALGraphG,intearly[],intlater[]);
//输出事件的最早发生时间与最迟发生时间
voidcalculater_Critical_path(ALGraphG,intearly[],intlater[]);
//根据事件的最早发生时间和最迟发生时间求得关键路径
voidcalculate_event_later(ALGraphG,intlater[],inttopo[]);
//计算事件的最迟发生时间
voidcalculate_event_early(ALGraphG,intearly[],inttopo[]);
//计算事件的最早发生时间
voidDestory_lowTree(PlowestTree*tree);
//销毁最小生成树
voiddirection_graph_control();
//有向图的操作
voidun_direction_graph_control();
//无向图的操作
main()
{
inti=0;
while(i!
=3)
{
printf("**********************************\n");
printf("无向图操作菜单按1\n");
printf("有向图操作菜单按2\n");
printf("退出应用程序按3\n");
printf("**********************************\n");
printf("请选择你要进行的操作:
");
scanf("%d",&i);
switch(i)
{
case1:
un_direction_graph_control();
break;
case2:
direction_graph_control();
break;
}
if(i==3)
{
break;
}
if(i<1||i>3)
{
printf("\n你选择的操作有误,请参照以下菜单选择操作!
\n");
}
}
}
/*
*函数功能:
输出最小生成树的具体边组成
*入口参数为最小生成树
*无返回值
*/
voidprintf_lowTree(PlowestTreeTree)
{
PlowestTreep;
p=Tree->next;
printf("\n最小生成树为:
\n");
//%c----%cp->vertex1,p->vertex2
printf("边权值\n");
while(p)
{
printf("%c----%c%d\n",p->vertex1,p->vertex2,p->info);
p=p->next;
}
printf("\n");
}
/*
*函数功能:
输出以邻接表方式存储的图
*入口参数为邻接表表示的图
*无返回值
*/
voidprintf_ALGraph(ALGraphALG)
{
intk;
EdgeNode*p;
printf("\n邻接表表示的图为:
\n");
for(k=0;k{
p=ALG.adjlist[k].fristEdge;
if(p)
{
printf("%c-->",ALG.adjlist[k].vertex);
}
else
{
printf("%c",ALG.adjlist[k].vertex);
}
while(p)
{
if(p->next)
{
printf("%c-->",ALG.adjlist[p->adjvertex].vertex);
}
else
{
printf("%c",ALG.adjlist[p->adjvertex].vertex);
}
//说明:
这里输出的是顶点信息
p=p->next;
}
printf("\n");
}
printf("\n");
}
/*创建一个有向图,函数中会询问是否给边带权值*/
ALGraphcreate_DGraph()
{
ALGraphDG;
intn,e;
intk;
VertexTypevt;
printf("请输入要建立有向图的顶点数:
");
scanf("%d",&n);
printf("请输入要该图的边数:
");
scanf("%d",&e);
DG.n=n;
DG.e=e;
//给图赋值相应的顶点数及边数
for(k=0;k{
fflush(stdin);
printf("请输入第%d个顶点的信息:
",k+1);
scanf("%c",&vt);
DG.adjlist[k].vertex=vt;
DG.adjlist[k].fristEdge=NULL;
}
//创建顶点的信息
printf("\n\t请选择你要创建的无向图:
\n");
printf("**************************************\n");
printf("带权的有向图按1\n");
printf("不带权的有向图按2\n");
printf("**************************************\n");
scanf("%d",&k);
if(k==1)
{
catch_Dvalues(&DG);
}
else
{
uncatch_Dvalues(&DG);
}
//上面的if--else中调用的函数必须是传地址的否则不会真正的赋值
returnDG;
}
/*为有向图创建边并且为边赋权值*/
voidcatch_Dvalues(ALGraph*DG)
{
intk,i,j;
EdgeNode*en;
EdgeTypeet;
printf("\n注意顶点的信息,如果是从0开始的则没有问题!
否则边的两个端点要减一");
printf(",也就是说保证从0开始!
\n");
for(k=0;k<(*DG).e;k++)
{
printf("请输入第%d条边所关联的两个顶点:
",k+1);
scanf("%d%d",&i,&j);
printf("请输入该边所带的权值:
");
scanf("%d",&et);
en=(EdgeNode*)malloc(sizeof(EdgeNode));
en->info=et;
en->adjvertex=j;
en->next=(*DG).adjlist[i].fristEdge;
(*DG).adjlist[i].fristEdge=en;
//因为图为有向图,所以只要关联一次.要区别无向图的创建
}
}
/*为有向图创建边并且不为边赋权值*/
voiduncatch_Dvalues(ALGraph*DG)
{
intk,i,j;
EdgeNode*en;
printf("\n注意顶点的信息,如果是从0开始的则没有问题!
否则边的两个端点要减一");
printf(",也就是说保证从0开始!
\n");
for(k=0;k<(*DG).e;k++)
{
printf("请输入第%d条边所关联的两个顶点:
",k+1);
scanf("%d%d",&i,&j);
en=(EdgeNode*)malloc(sizeof(EdgeNode));
en->adjvertex=j;
en->next=(*DG).adjlist[i].fristEdge;
(*DG).adjlist[i].fristEdge=en;
}
}
/*
*函数功能:
创建一个无向图
*不需要参数
*返回值为无向图
*/
ALGraphcreate_undALGraph()
{
ALGraphALG;
intn,e;
intk;
VertexTypevt;
printf("请输入要建立无向图的顶点数:
");
scanf("%d",&n);
printf("请输入要该图的边数:
");
scanf("%d",&e);
ALG.n=n;
ALG.e=e;
for(k=0;k{
fflush(stdin);
printf("请输入第%d个顶点的信息:
",k+1);
scanf("%c",&vt);
ALG.adjlist[k].vertex=vt;
ALG.adjlist[k].fristEdge=NULL;
}
printf("\n\t请选择你要创建的无向图:
\n");
printf("**************************************\n");
printf("带权的无向图按1\n");
printf("不带权的无向图按2\n");
printf("**************************************\n");
scanf("%d",&k);
if(k==1)
{
catch_values(&ALG);
}
else
{
uncatch_values(&ALG);
}
//上面的if--else中调用的函数必须是传地址的否则不会真正的赋值
returnALG;
}
/*
*函数功能:
不为图中的边带权,即创建图时,边没有带权
*入口参数为指向图的指针
*无返回值
*/
voiduncatch_values(ALGraph*ALG)
{
intk,i,j;
EdgeNode*en;
tag=false;
printf("\n注意顶点的信息,如果是从0开始的则没有问题!
否则边的两个端点要减一");
printf(",也就是说保证从0开始!
\n");
for(k=0;k<(*ALG).e;k++)
{
printf("请输入第%d条边所关联的两个顶点:
",k+1);
scanf("%d%d",&i,&j);
en=(EdgeNode*)malloc(sizeof(EdgeNode));
en->adjvertex=j;
en->next=(*ALG).adjlist[i].fristEdge;
(*ALG).adjlist[i].fristEdge=en;
//因为图为无向图,所以要两次分配单元
en=(EdgeNode*)malloc(sizeof(EdgeNode));
en->adjvertex=i;
en->next=(*ALG).adjlist[j].fristEdge;
(*ALG).adjlist[j].fristEdge=en;
}
}
/*
*函数功能:
为无向图中的边带权,即创建图时,边带有权值
*入口参数为指向图的指针
*无返回值
*/
voidcatch_values(ALGraph*ALG)
{
intk,i,j;
EdgeNode*en;
EdgeTypeet;
printf("\n注意顶点的信息,如果是从0开始的则没有问题!
否则边的两个端点要减一");
printf(",也就是说保证从0开始!
\n");
tag=true;
for(k=0;k<(*ALG).e;k++)
{
printf("请输入第%d条边所关联的两个顶点:
",k+1);
scanf("%d%d",&i,&j);
printf("请输入该边所带的权值:
");
scanf("%d",&et);
//以下要关联两次,因为无向图的边没有方向
en=(EdgeNode*)malloc(sizeof(EdgeNode));
en->info=et;
en->adjvertex=j;
en->next=(*ALG).adjlist[i].fristEdge;
(*ALG).adjlist[i].fristEdge=en;
en=(EdgeNode*)malloc(sizeof(EdgeNode));
en->info=et;
en->adjvertex=i;
en->next=(*ALG).adjlist[j].fristEdge;
(*ALG).adjlist[j].fristEdge=en;
}
}
/*
*函数功能:
深度遍历图
*说明:
此函数中还调用DFS(ALGraphALG,intv)函数
*入口参数为图
*无返回值
*/
voiddepth_Graph(ALGraphALG)
{
intk;
intcount=0;
for(k=0;k{
visited[k]=false;
}//标志每个顶点未被访问
printf("\n深度遍历图为:
\n");
for(k=0;k{
if(!
visited[k])
{
count++;
DFS(ALG,k);
}
}
printf("\n该图连通分量为:
%d\n",count);
}
/*
*函数功能:
深度遍历图
*入口参数ALG为图,v为遍历开始的节点
*无返回值
*/
voidDFS(ALGraphALG,intv)
{
EdgeNode*p;
printf("%c",ALG.adjlist[v].vertex);
visited[v]=true;
p=ALG.adjlist[v].fristEdge;
while(p)
{
if(!
visited[p->adjvertex])
{
DFS(ALG,p->adjvertex);
}
p=p->next;
}
}
/*
*函数功能:
广度遍历图