2019.6.24-2019.6.28(实训数据结构) 书籍:《数据结构项目实训教程》 赵君喆,戴文华
7.1图的邻接矩阵表示
开发一个用邻接矩阵构造的图的操作程序,要求兼容有向图、无向图、有向网、无向网的创建和操作,程序的接口见主函数。
#include<cstdio> #include<stdlib.h> #include<string.h> //#include<cunity> #include<iostream> #include<string> #include<fstream> #include<iomanip> #include<algorithm> using namespace std; #define OK 1 #define ERROR -1 #define OVERFLOW -2 #define Max 100 typedef int Status; #define INFINITY INT_MAX //用整型最大值代替无穷 #define MAX_VERTEX_NUM 50 //最大顶点个数 #define MAX_NAME 6 //顶点字符串的最大长度+1 #define MAX_INFO 20 //相关信息字符串的最大长度+1 typedef int VRType; //顶点关系类型 typedef char InfoType; //相关信息类型 typedef char VertexType[MAX_NAME]; // 顶点类型 typedef struct{ VRType adj; //无权图用1或0表示相邻否;带权图在这里表示为权值 InfoType *info; //该弧相关信息的指针 }ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; enum GraphKind{DG,DN,UDG,UDN}; bool visited[MAX_VERTEX_NUM]; //访问标志数组 struct MGraph{ VertexType vexs[MAX_VERTEX_NUM]; //顶点向量 AdjMatrix arcs; //邻接矩阵 int vexnum; //图的当前顶点数 int arcnum; //图的当前弧度数 GraphKind kind; //图的种类坐标数 }; int LocateVex(MGraph G,VertexType v){ //初始条件:图G存在,u和G顶点有相同特征 //操作结果:若图G中存在顶点u,则返回该顶点在图中的位置;否则返回-1 int i; for(i=0;i<G.vexnum;i++){ if(strcmp(v,G.vexs[i])==0){ return i; } } return -1; } Status InsertArc(MGraph &G,VertexType v,VertexType w){ //初始条件:图G存在,v和w是G中的两个顶点 //操作结果:在G中增添弧<v,w>,若G为无向,则增添对称弧<w,v> int i,l,v1,w1; char s[MAX_INFO]; v1=LocateVex(G,v); //尾 w1=LocateVex(G,w); //头 if(v1<0||w1<0) //v1,w1的值不合法 return ERROR; G.arcnum++; // 弧或边数加1 if(G.kind%2){ //网 printf("请输入此弧或边的权值:"); scanf("%d",&G.arcs[v1][w1].adj); } else{ //图 G.arcs[v1][w1].adj=1; } printf("是否有该弧边的相关信息(0:无,1:有):"); scanf("%d",&i); fflush(stdin); // 清除缓冲区中残留的回车字符 if(i){ printf("请输入弧的相关信息(少于%d个字符):",MAX_INFO); gets(s); l=strlen(s); if(1){ G.arcs[v1][w1].info=(char*)malloc((l+1)*sizeof(char)); strcpy(G.arcs[v1][w1].info,s); } } if(G.kind>1){ //无向 //指向同一个相关信息 G.arcs[w1][v1].adj=G.arcs[v1][w1].adj; G.arcs[w1][v1].info=G.arcs[v1][w1].info; } return OK; } void CreateGraph(MGraph &G){ //操作结果:采用数组(邻接矩阵)表示法,构造图G int i,j,k,arcnum; VertexType va,vb; printf("输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3):"); scanf("%d",&G.kind); printf("请输入图的顶点数和弧度数(以空格作为间隔):"); scanf("%d%d",&G.vexnum,&arcnum); if(G.vexnum>=MAX_VERTEX_NUM-1){ //若输入顶点数超过图G的最大容量,则以最大容量作为顶点数 G.vexnum=MAX_VERTEX_NUM-1; } printf("请输入%d个顶点值(少于%d个字符): ",G.vexnum,MAX_NAME); for(i=0;i<G.vexnum;i++){ //构造顶点向量 scanf("%s",G.vexs[i]); } for(i=0;i<G.vexnum;i++){ //初始化邻接矩阵 for(j=0;j<G.vexnum;j++){ if(G.kind%2) //网 G.arcs[i][j].adj=INFINITY; else //图 G.arcs[i][j].adj=0; G.arcs[i][j].info=NULL; } } G.arcnum=0; printf("请输入%d条弧的弧尾、弧头(以空格作为间隔): ",arcnum); for(k=0;k<arcnum;k++){ //依次增加弧 scanf("%s%s",va,vb); InsertArc(G,va,vb); } } void DestroyGraph(MGraph &G){ //初始条件:图G存在 //操作结果:销毁图G int i,j,k=0; if(G.kind%2){ //网 k=INFINITY; //k为两个顶点之间无边或弧时邻接矩阵元素的值 } for(i=0;i<G.vexnum;i++){ //释放弧或边的相关信息 if(G.kind<2){ //有向 for(j=0;j<G.vexnum;j++){ if(G.arcs[i][j].adj!=k){ //有弧 if(G.arcs[i][j].info){ //有相关信息 free(G.arcs[i][j].info); G.arcs[i][j].info=NULL; } } } } else{ //无向 for(j=i+1;j<G.vexnum;j++){ //只查上三角元素 if(G.arcs[i][j].adj!=k){ //有边 if(G.arcs[i][j].info){ //有相关信息 free(G.arcs[i][j].info); G.arcs[i][j].info=NULL; G.arcs[j][i].info=NULL; } } } } } G.vexnum=0; //顶点数为0 G.arcnum=0; //边数为0 } Status PutVex(MGraph &G,VertexType v,VertexType value){ //初始条件:图G存在,v是G中某个顶点 //操作结果:对v赋新值value int k; k=LocateVex(G,v) ; //k为顶点v在图G中的序号 if(k<0){ return ERROR; } strcpy(G.vexs[k],value); return OK; } void InsertVex(MGraph &G,VertexType v){ //初始条件:图G存在,u和G顶点有相同特征 //操作结果:在图G中增添新顶点v int i,j=0; if(G.vexnum>=MAX_VERTEX_NUM-1) //若顶点数已达到最大容量,则不能插入此顶点 return; if(G.kind%2) //网 j=INFINITY; strcpy(G.vexs[G.vexnum],v); //构造新顶点向量 for(i=0;i<=G.vexnum;i++){ //初始化新增行、新增列邻接矩阵的值(无边或弧) G.arcs[G.vexnum][i].adj=j; G.arcs[i][G.vexnum].adj=j; //初始化相关信息指针 G.arcs[G.vexnum][i].info=NULL; G.arcs[i][G.vexnum].info=NULL; } G.vexnum++; //图G的顶点数加1 } Status DeleteVex(MGraph &G,VertexType v){ //初始条件:图G存在,v是G中的某个顶点 //操作结果:删除G中顶点v及相关的弧 int i,j,k; VRType m=0; if(G.kind%2) //网 m=INFINITY; k=LocateVex(G,v); // k为待删除顶点v的序号 if(k<0) //v不是图G的顶点 return ERROR; for(j=0;j<G.vexnum;j++){ if(G.arcs[j][k].adj!=m){ //有入弧或边 if(G.arcs[j][k].info){ //有相关信息 free(G.arcs[j][k].info); //释放相关信息 } G.arcnum--; //修改弧数 } } if(G.kind<2) //有向 for(j=0;j<G.vexnum;j++){ if(G.arcs[k][j].adj!=m){ //有出弧 if(G.arcs[k][j].info){ //有相关信息 free(G.arcs[k][j].info); //释放相关信息 } G.arcnum--; //修改弧数 } } for(j=k+1;j<G.vexnum;j++){ //序号k后面的顶点向量依次前移 strcpy(G.vexs[j-1],G.vexs[j]); } for(i=0;i<G.vexnum;i++){ for(j=k+1;j<G.vexnum;j++){ //移动待删除顶点之右的矩阵元素 G.arcs[i][j-1]=G.arcs[i][j]; } } for(i=0;i<G.vexnum;i++){ for(j=k+1;j<G.vexnum;j++){ //移动待删除顶点之下的矩阵元素 G.arcs[j-1][i]=G.arcs[j][i]; } } G.vexnum--; //更新图的顶点数 return OK; } Status DeleteArc(MGraph &G,VertexType v,VertexType w){ //初始条件:图G存在,v和w是G中的两个顶点 //操作结果:在G中增删除<v,w>,若G为无向,还需删除对称弧<w,v> int v1,w1,j=0; if(G.kind%2) //网 j=INFINITY; v1=LocateVex(G,v); //尾 w1=LocateVex(G,w); //头 if(v1<0||w1<0) //v1,w1的值不合法 return ERROR; G.arcs[v1][w1].adj=j; if(G.arcs[v1][w1].info){ //有其他信息 free(G.arcs[v1][w1].info); G.arcs[v1][w1].info=NULL; } if(G.kind>=2){ //无向,删除对称弧 G.arcs[w1][v1].adj=j; G.arcs[w1][v1].info=NULL; } G.arcnum--; //弧数-1 return OK; } int FirstAdjVex(MGraph G,VertexType v){ //初始条件:图G存在,v是G中某个顶点 //操作结果:返回v的第一个邻接顶点的序号;若没有则返回-1 int i,j=0,k; k=LocateVex(G,v); //k为顶点v在图G中的序号 if(k<0){ //顶点不存在 return -1; } if(G.kind%2) //网 j=INFINITY; for(i=0;i<G.vexnum;i++){ if(G.arcs[k][i].adj!=j) return i; } return -1; } int NextAdjVex(MGraph G,VertexType v,VertexType w){ //初始条件:图G存在,v和w是G中某个顶点 //操作结果:返回w的序号;若没有则返回-1 int i,j=0,k1,k2; k1=LocateVex(G,v); //k为顶点v在图G中的序号 k2=LocateVex(G,w); if(k1<0||k2<0){ //顶点不存在 return -1; } if(G.kind%2) //网 j=INFINITY; for(i=k2+1;i<G.vexnum;i++){ if(G.arcs[k1][i].adj!=j) return i; } return -1; } void visitVex(MGraph G,int v){ cout<<G.vexs[v]<<" "; } void DFS(MGraph G,int v){ //操作结果:从第v个顶点出发递归地深度优先遍历图G int w; visited[v]=true; //设置访问标志为TURE(已访问) visitVex(G,v); //访问第v个顶点 //cout<<"dsssssssssscs"<<endl; w=FirstAdjVex(G,G.vexs[v]); while(w>=0){ if(!visited[w]){ //对v的尚未访问的序号为w的邻接顶点递归调用DFS DFS(G,w); } w=NextAdjVex(G,G.vexs[v],G.vexs[w]); } } void DFSTraverse(MGraph G){ //初始条件:图G存在,Visit是顶点的应用函数 //操作结果:从第1个顶点起,深度优先遍历图G,并对每个顶点调用函数Visit一次且仅调用一次 int v; for(v=0;v<G.vexnum;v++){ visited[v]=false; //访问标志数组初始化(未被访问) } for(v=0;v<G.vexnum;v++){ if(!visited[v]) DFS(G,v); //对尚未访问的顶点v调用DFS } } typedef int SElemType; typedef struct QNode{ SElemType data; QNode *next; }*QueuePtr; struct LinkQueue{ QueuePtr front,rear;//队头、队尾指针 }; Status InitQueue(LinkQueue &Q){ if(!(Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode)))){ exit(OVERFLOW); } Q.front->next=NULL; return OK; } Status EnQueue(LinkQueue &Q,SElemType e){ QueuePtr p; if(!(p=(QueuePtr)malloc(sizeof(QNode)))){ exit(OVERFLOW); } p->data=e; p->next=NULL; Q.rear->next=p; Q.rear=p; return OK; } SElemType DeQueue(LinkQueue &Q,SElemType e){ QueuePtr p; if(Q.front==Q.rear){ return ERROR; } p=Q.front->next; e=p->data; Q.front->next=p->next; if(Q.rear==p){ Q.rear=Q.front; } free(p); return OK; } Status QueueEmpty(LinkQueue Q){ if(Q.front==Q.rear) return 1; else return 0; } void BFSTraverse(MGraph G){ //初始条件:图G存在,Visit是顶点的应用函数 //操作结果:从第1个顶点起,广度优先非递归遍历图G,并对每个顶点调用函数Visit一次且仅调用一次 int u,v,w; LinkQueue Q; //使用辅助队列Q和访问标志数组visited for(v=0;v<G.vexnum;v++){ visited[v]=false; // 置初值 } InitQueue(Q); //置空的辅助队列Q for(v=0;v<G.vexnum;v++){ if(!visited[v]){ //尚未访问 visited[v]=1; //设置访问标志为TRUE(已访问) visitVex(G,v); EnQueue(Q,v); //v入队列 while(!QueueEmpty(Q)){ //队列不为空 DeQueue(Q,u); //队头元素出队并置为u w=FirstAdjVex(G,G.vexs[u]); while(w>=0){ if(!visited[w]){ //w为u的尚未访问的邻接顶点的序号 visited[w]=1; visitVex(G,w); EnQueue(Q,w); } w=NextAdjVex(G,G.vexs[u],G.vexs[w]); } } } } } void Display(MGraph G){ //操作结果:输出邻接矩阵存储表示的图 int i,j; char s[7]; switch(G.kind){ case DG: strcpy(s,"有向图"); break; case DN: strcpy(s,"有向网"); break; case UDG: strcpy(s,"无向图"); break; case UDN: strcpy(s,"无向网"); } printf("%d个顶点%d条边或弧的%s。顶点依次是:",G.vexnum,G.arcnum,s); for(i=0;i<G.vexnum;i++) //输出G.vexs printf("%s ",G.vexs[i]); printf(" G.arcs.adj: "); //输出G.arcs.adj for(i=0;i<G.vexnum;i++){ for(j=0;j<G.vexnum;j++){ printf("%lld ",G.arcs[i][j].adj); } printf(" "); } printf("G.arcs.info: "); //输出G.arcs.info printf("顶点1(弧尾) 顶点2(弧头) 该边或弧的信息: "); for(i=0;i<G.vexnum;i++){ if(G.kind<2){ for(j=0;j<G.vexnum;j++){ //有向 if(G.arcs[i][j].adj>0&&G.arcs[i][j].adj<INFINITY){ printf("%5s %11s %s ",G.vexs[i],G.vexs[j],G.arcs[i][j].info); } } } else{ //无向,输出上三角元素 for(j=i+1;j<G.vexnum;j++){ //有向 if(G.arcs[i][j].adj>0&&G.arcs[i][j].adj<INFINITY){ printf("%5s %11s %s ",G.vexs[i],G.vexs[j],G.arcs[i][j].info); } } } } } int main(){ int choose; MGraph G; CreateGraph(G); VertexType v,w,value; cout<<" 欢迎来到邻接矩阵构造系统!!!!!" <<endl; cout<<" 请按以下数字操作" <<endl; cout<<"----------------------------------1.输出图信息----------------------------------"<<endl; cout<<"----------------------------------2.查找顶点号----------------------------------"<<endl; cout<<"----------------------------------3.插入顶点------------------------------------"<<endl; cout<<"----------------------------------4.删除顶点------------------------------------"<<endl; cout<<"----------------------------------5.修改顶点值----------------------------------"<<endl; cout<<"----------------------------------6.增加弧--------------------------------------"<<endl; cout<<"----------------------------------7.删除弧--------------------------------------"<<endl; cout<<"----------------------------------8.深度优先遍历--------------------------------"<<endl; cout<<"----------------------------------9.广度优先遍历--------------------------------"<<endl; cout<<"----------------------------------0.退出程序------------------------------------"<<endl<<endl; choose=-1; while(choose!=0){ cout<<"--------------------------------------------------------------------------------"<<endl; cout<<"请选择:"<<endl; cin>>choose; switch(choose){ case 0: cout<<"您已经成功退出系统,欢迎您下次再来!"<<endl; break; case 1: Display(G); cout<<"您已经成功输出图信息!"<<endl; break; case 2: printf("请输入要查询的顶点: "); scanf("%s",&v); printf("顶点%s查询的顶点号为%d ",v,LocateVex(G,v)); break; case 3: printf("请输入要插入的顶点: "); scanf("%s",&v); InsertVex(G,v); printf("您已经成功插入顶点! "); break; case 4: printf("请输入要删除的顶点: "); scanf("%s",&v); DeleteVex(G,v); printf("您已经成功删除顶点! "); break; case 5: printf("请输入要修改的顶点: "); scanf("%s",&v); printf("请输入要修改的值: "); scanf("%s",&value); PutVex(G,v,value); printf("您已经成功修改顶点值! "); break; case 6: printf("请输入要增加弧的两个的顶点(以空格分隔): "); scanf("%s%s",&v,&w); InsertArc(G,v,w); printf("您已经成功增加弧! "); break; case 7: printf("请输入要删除弧的两个的顶点(以空格分隔): "); scanf("%s%s",&v,&w); DeleteArc(G,v,w); printf("您已经成功删除弧! "); break; case 8: DFSTraverse(G) ; printf("您已经成功深度优先遍历! "); break; case 9: BFSTraverse(G); printf("您已经成功广度优先遍历! "); break; } } return 0; }