无向图的邻接多重表结构,存储结构及基本功能实现(最全)

来源:互联网 发布:手机身份证照片软件 编辑:程序博客网 时间:2024/05/21 13:11

邻接多重表结构主要针对的是无向图进行的存储优化。详细概念可百度谷歌下。如果我们在无向图的应用中,关注的重点是顶点,那么邻接表是不错的选择,但如果我们更关注边的操作,比如对已访问过的边做标记,删除某一条边等操作,那就意味着,需要找到这条边的两个边表结点进行操作,这其实还是比较麻烦的。因此,我们也依照十字链表的方式,对表结点进行一下改造:
                        [ ivex | ilink | jvex | jlink]

其中ivex和jvex是与某条边依附的两个顶点在顶点表中下标。ilink指向依附点ivex的下一条边,jlink指向依附点jvex的下一条边,这就是邻接多重表结构。关于邻接多重表的一些相关操作,多数网站上都没有具体的介绍,本人的代码主要参考严魏敏的 <数据结构>,但是本书中提供的代码有一定的问题(尤其是删除节点及其相关的边这一函数,弄了很长时间才把问题找出来并改正,神坑啊),以下是改正后的代码,我又添加了一些基本功能,希望可以给大家参考下:

在MulAdjGraph.h文件中:

/********************************* 声明区 ****************************/typedef enum{unmarked,marked}MarkedIf;typedef int VertexType; // 图节点标识号 typedef int QElemType;/********************************* 数据结构体 ****************************/typedef struct ArcType{intivex,jvex;// 该边依附的两个顶点的位置 struct ArcType *ilink,*jlink;// 分别指向依附这两个顶点的下一条边MarkedIf  mark;// 访问标记floatweight;// 权重 inline ArcType(): ivex(-1),jvex(-1),mark(unmarked),weight(0) {}}ArcType;typedef struct VertexNode{VertexType data;boolisVisited;// 访问标记ArcType*firstarc;// 指向第一条依附该顶点的边 inline VertexNode():data(-1),isVisited(false) {}}VertexNode;typedef struct MulAdjGraph{VertexNode *adjmulist;// 多重邻接表int ivexNum,iarcNum;// 无向图的当前顶点数和边数 int MaxVertexNum;// 表中最大的节点数量MulAdjGraph(): ivexNum(0), iarcNum(0),MaxVertexNum(0) {}}MulAdjGraph;// 单链队列--队列的链式存储结构 typedef struct QNode{QElemType data;//数据域struct QNode *next;//指针域}QNode,*QueuePtr;typedef struct{QueuePtr front,//队头指针,指针域指向队头元素 rear; //队尾指针,指向队尾元素}LinkQueue;/********************************* 基本操作函数 ****************************/// 若G中存在顶点u,则返回该顶点在无向图中位置;否则返回-1int LocateVex( const MulAdjGraph &G,const VertexType &u );// 采用邻接多重表存储结构,构造无向图Gint CreateGraph(MulAdjGraph &G);// 返回v的值inline VertexType* GetVex(const MulAdjGraph &G,const int &v);// 对v赋新值valueint PutVex(MulAdjGraph &G,const VertexType v,const VertexType &value);// 返回v的第一个邻接顶点的序号。若顶点在G中没有邻接顶点,则返回-1 int FirstAdjVex(const MulAdjGraph &G,const VertexType &v);// 返回v的(相对于w的)下一个邻接顶点的序号。若w是v的最后一个邻接点,则返回-1 int NextAdjVex(const MulAdjGraph &G,const VertexType &v,const VertexType &w);// 在G中增添新顶点v(不增添与顶点相关的弧,留待InsertArc()去做)int InsertVex(MulAdjGraph &G,const VertexType v);// 在G中删除弧<v,w>int DeleteArc(MulAdjGraph &G,const VertexType &v,const VertexType &w);//  删除G中顶点v及其相关的边 int DeleteVex(MulAdjGraph &G,const VertexType &v);void DestroyGraph(MulAdjGraph &G);// 在G中增添弧<v,w>int InsertArc(MulAdjGraph &G,const VertexType &v,const VertexType &w);//搜索弧<v,w>是否存在bool SearchArc(const MulAdjGraph &G,const VertexType &v,const VertexType &w);//找到顶点v的邻接顶点,函数返回符合条件的顶点数目int FindNeighbour(const MulAdjGraph &G,const VertexType &v,VertexType *s);extern int(*VisitFunc)(const VertexType &v);void DFS(MulAdjGraph &G,int v);// 算法7.4 // 从第1个顶点起,深度优先遍历图G,并对每个顶点调用函数Visit void DFSTraverse(MulAdjGraph &G,int(*visit)(const VertexType&));// 从第1个顶点起,按广度优先非递归遍历图G,并对每个顶点调用函数 // Visit一次且仅一次。使用辅助队列Q和访问标志数组visite void BFSTraverse(MulAdjGraph &G,int(*Visit)(const VertexType&));// 置边的访问标记为未被访问 void MarkUnvizited(MulAdjGraph &G);// 输出无向图的邻接多重表Gvoid Display(MulAdjGraph &G);int visit(const VertexType &v);
MulAdjGraph.cpp文件中

<p>#include "MulAdjGraph.h"</p>#include <stdio.h>#include <stdlib.h>// 若G中存在顶点u,则返回该顶点在无向图中位置;否则返回-1int LocateVex( const MulAdjGraph &G,const VertexType &u ){ int i;for(i=0;i<G.ivexNum;++i)if( u==G.adjmulist[i].data )return i;return -1;}// 采用邻接多重表存储结构,构造无向图Gint CreateGraph(MulAdjGraph &G){ int i,j,OverInfo,IncInfo;VertexType first,second;printf("输入无向图G的顶点数,边数,边是否含权重信息(是:1,否:0):");scanf("%d,%d,%d",&G.ivexNum,&G.iarcNum,&OverInfo);G.MaxVertexNum = G.ivexNum;G.adjmulist = new VertexNode[G.MaxVertexNum];printf("请输入%d个顶点的标识号(标识号为int数据类型):\n",G.ivexNum);for(i=0;i<G.ivexNum;++i)// 构造顶点向量 {scanf("%d",&G.adjmulist[i].data);G.adjmulist[i].firstarc = NULL;}printf("请输入由两点构成的边%d条(以空格作为间隔):\n",G.iarcNum);for(int k=0;k<G.iarcNum;++k)// 构造表结点链表 {scanf("%d %d%*c",&first,&second);// %*c吃掉回车符 i=LocateVex(G,first);// 一端 j=LocateVex(G,second);// 另一端 if ( i == -1 || j == -1 )   return 0;  ArcType* pArc = new ArcType;pArc->ivex = i;//赋值,权值,访问属性暂时不用pArc->jvex = j;pArc->ilink=G.adjmulist[i].firstarc;// 插在表头 G.adjmulist[i].firstarc=pArc;pArc->jlink=G.adjmulist[j].firstarc;// 插在表头 G.adjmulist[j].firstarc=pArc;if (OverInfo){printf("该边是否有权重(1:有 0:无): ");scanf("%d%*c",&IncInfo);// 吃掉回车符 if(IncInfo)// 边有相关信息 {printf("请输入该边的权值:");scanf("%f%*c",&pArc->weight); // 吃掉回车符 }}}return 1;}// 返回v的值inline VertexType* GetVex(const MulAdjGraph &G,const int &v){ if(v>=G.ivexNum||v<0)      exit(0);return &G.adjmulist[v].data;}// 对v赋新值valueint PutVex(MulAdjGraph &G,const VertexType v,const VertexType &value){  int i;i=LocateVex(G,v);if(i<0) // v不是G的顶点 return 0;G.adjmulist[i].data=value;return 1;}// 返回v的第一个邻接顶点的序号。若顶点在G中没有邻接顶点,则返回-1 int FirstAdjVex(const MulAdjGraph &G,const VertexType &v){ int i;i=LocateVex(G,v);if(i<0)return -1;if(G.adjmulist[i].firstarc) // v有邻接顶点 if(G.adjmulist[i].firstarc->ivex==i)return G.adjmulist[i].firstarc->jvex;elsereturn G.adjmulist[i].firstarc->ivex;elsereturn -1;}// 返回v的(相对于w的)下一个邻接顶点的序号。若w是v的最后一个邻接点,则返回-1 int NextAdjVex(const MulAdjGraph &G,const VertexType &v,const VertexType &w){ int i,j;ArcType *p;i=LocateVex(G,v); // i是顶点v的序号 j=LocateVex(G,w); // j是顶点w的序号 if(i<0||j<0) // v或w不是G的顶点 return -1;p=G.adjmulist[i].firstarc; // p指向顶点v的第1条边 while(p)if(p->ivex==i&&p->jvex!=j) // 不是邻接顶点w(情况1) p=p->ilink; // 找下一个邻接顶点 else if(p->jvex==i&&p->ivex!=j) // 不是邻接顶点w(情况2) p=p->jlink; // 找下一个邻接顶点 else // 是邻接顶点w break;if(p&&p->ivex==i&&p->jvex==j) // 找到邻接顶点w(情况1) {p=p->ilink;if(p&&p->ivex==i)return p->jvex;else if(p&&p->jvex==i)return p->ivex;}if(p&&p->ivex==j&&p->jvex==i) // 找到邻接顶点w(情况2) {p=p->jlink;if(p&&p->ivex==i)return p->jvex;else if(p&&p->jvex==i)return p->ivex;}return -1;}// 在G中增添新顶点v(不增添与顶点相关的弧,留待InsertArc()去做)int InsertVex(MulAdjGraph &G,const VertexType v){  if(G.ivexNum==G.MaxVertexNum)// 结点已满,不能插入 {printf("结点已满,不能插入.\n");return 0;}if(LocateVex(G,v)>=0)// 结点已存在,不能插入 {printf("结点已存在,不能插入.\n");return 0;}G.adjmulist[G.ivexNum].data = v;G.adjmulist[G.ivexNum].firstarc=NULL;G.ivexNum++;return 1;}// 在G中删除弧<v,w>int DeleteArc(MulAdjGraph &G,const VertexType &v,const VertexType &w){  int i,j;ArcType *p,*q;i=LocateVex(G,v);j=LocateVex(G,w);if(i<0||j<0||i==j)     return 0;// 图中没有该点或弧 // 以下使指向待删除边的第1个指针绕过这条边 p=G.adjmulist[i].firstarc;// p指向顶点v的第1条边 if(p&&p->jvex==j)// 第1条边即为待删除边(情况1) G.adjmulist[i].firstarc=p->ilink;else if(p&&p->ivex==j)// 第1条边即为待删除边(情况2) G.adjmulist[i].firstarc=p->jlink;else// 第1条边不是待删除边 {while(p)// 向后查找弧<v,w> {if(p->ivex==i&&p->jvex!=j) // 不是待删除边 {q=p;p=p->ilink; // 找下一个邻接顶点 }else if(p->jvex==i&&p->ivex!=j) // 不是待删除边 {q=p;p=p->jlink; // 找下一个邻接顶点 }else // 是邻接顶点w break;}if(!p) // 没找到该边 return 0;if(p->ivex==i&&p->jvex==j) // 找到弧<v,w>(情况1) if(q->ivex==i)q->ilink=p->ilink;elseq->jlink=p->ilink;else if(p->ivex==j&&p->jvex==i) // 找到弧<v,w>(情况2) if(q->ivex==i)q->ilink=p->jlink;elseq->jlink=p->jlink;}// 以下由另一顶点起找待删除边且删除之 p=G.adjmulist[j].firstarc;// p指向顶点w的第1条边 if(p->jvex==i)// 第1条边即为待删除边(情况1) {G.adjmulist[j].firstarc=p->ilink;delete p;}else if(p->ivex==i)// 第1条边即为待删除边(情况2) {G.adjmulist[j].firstarc=p->jlink;delete p;}else // 第1条边不是待删除边 {while(p) // 向后查找弧<v,w> if(p->ivex==j&&p->jvex!=i) // 不是待删除边 {q=p;p=p->ilink; // 找下一个邻接顶点 }else if(p->jvex==j&&p->ivex!=i) // 不是待删除边 {q=p;p=p->jlink; // 找下一个邻接顶点 }else // 是邻接顶点v break;if(p->ivex==i&&p->jvex==j) // 找到弧<v,w>(情况1) {if(q->ivex==j)q->ilink=p->jlink;elseq->jlink=p->jlink;delete p;}else if(p->ivex==j&&p->jvex==i) // 找到弧<v,w>(情况2) {if(q->ivex==j)q->ilink=p->ilink;elseq->jlink=p->ilink;delete p;}}--G.iarcNum;return 1;}//  删除G中顶点v及其相关的边 int DeleteVex(MulAdjGraph &G,const VertexType &v){ int i,j;VertexType w;ArcType *p;i=LocateVex(G,v);// i为待删除顶点的序号 if(i<0)   return 0;for(j=0;j<G.ivexNum;++j)// 删除与顶点v相连的边(如果有的话) {if(j==i)continue;w=*GetVex(G,j);// w是第j个顶点的值 DeleteArc(G,v,w);}for(j=i+1;j<G.ivexNum;++j)// 排在顶点v后面的顶点的序号减1 G.adjmulist[j-1]=G.adjmulist[j];--G.ivexNum; // 顶点数减1 for(j=i;j<G.ivexNum;++j)// 修改顶点的序号 {p=G.adjmulist[j].firstarc;while(p){if(p->ivex==j+1){p->ivex--;p=p->ilink;}else{p->jvex--;p=p->jlink;}}}return 1;}void DestroyGraph(MulAdjGraph &G){int i;for(i=G.ivexNum-1;i>=0;--i)DeleteVex(G,G.adjmulist[i].data);delete [](G.adjmulist);}// 在G中增添弧<v,w>int InsertArc(MulAdjGraph &G,const VertexType &v,const VertexType &w){  int i,j,IncInfo;i=LocateVex(G,v); // 一端 j=LocateVex(G,w); // 另一端 if(i<0||j<0)return 0;ArcType* pArc = new ArcType;pArc->ivex=i;pArc->jvex=j;pArc->ilink=G.adjmulist[i].firstarc; // 插在表头 G.adjmulist[i].firstarc=pArc;pArc->jlink=G.adjmulist[j].firstarc; // 插在表头 G.adjmulist[j].firstarc=pArc;//printf("该边是否有权重(1:有 0:无): ");//scanf("%d%*c",&IncInfo); // 吃掉回车符 //if(IncInfo) // 边有相关信息 //{//printf("请输入该边的权值:");//scanf("%f%*c",&pArc->weight); // 吃掉回车符 //}G.iarcNum++;return 1;}//搜索弧<v,w>是否存在bool SearchArc(const MulAdjGraph &G,const VertexType &v,const VertexType &w ){int i,j;ArcType* p;i=LocateVex(G,v); // 一端 j=LocateVex(G,w); // 另一端 if(i<0||j<0||i==j)return false;//图中没有该弧p=G.adjmulist[i].firstarc;// p指向顶点v的第1条边 while(p)// 向后查找弧<v,w> {if(p->ivex==i&&p->jvex==j) //找到弧<v,w>(情况1) return true;else if(p->jvex==i&&p->ivex==j) // 找到弧<v,w>(情况2) return true;else if (p->ivex==i)p=p->ilink;else if (p->jvex==i)p=p->jlink;}return false;}//找到顶点v的邻接顶点,函数返回符合条件的顶点数目int FindNeighbour( const MulAdjGraph &G,const VertexType &v){int i,num=0;ArcType* p;i=LocateVex(G,v); // 一端 if (i<0) exit(0);//顶点v不存在p=G.adjmulist[i].firstarc;// p指向顶点v的第1条边 if (p){printf("顶点%d的邻接节点为:\n",v);while(p){if (p->ivex==i){printf("%4d ",G.adjmulist[p->jvex].data);++num;p=p->ilink;}else//p->jvex == i{printf("%4d ",G.adjmulist[p->ivex].data);++num;p=p->jlink;}}}elseprintf("该顶点无相邻顶点.\n");return num;}int(*VisitFunc)(const VertexType &v);void DFS(MulAdjGraph &G,int v){int j;ArcType *p;VisitFunc(G.adjmulist[v].data);G.adjmulist[v].isVisited=true;p=G.adjmulist[v].firstarc;while(p){j=p->ivex==v?p->jvex:p->ivex;if(!G.adjmulist[j].isVisited)DFS(G,j);p=p->ivex==v?p->ilink:p->jlink;}}// 算法7.4 // 从第1个顶点起,深度优先遍历图G,并对每个顶点调用函数Visit void DFSTraverse(MulAdjGraph &G,int(*visit)(const VertexType&)){int i;VisitFunc=visit;for(i=0;i<G.ivexNum;++i)G.adjmulist[i].isVisited=false;for(i=0;i<G.ivexNum;++i)if(!G.adjmulist[i].isVisited)DFS(G,i);printf("\n");}// 构造一个空队列Qint InitQueue(LinkQueue *Q){ (*Q).front=(*Q).rear=new QNode();//(*Q).front=(*Q).rear=(QueuePtr)malloc(sizeof(QNode));//动态分配一个空间if(!(*Q).front)exit(0);(*Q).front->next=NULL;//队头指针指向空,无数据域,这样构成了一个空队列return 1;}// 若Q为空队列,则返回1,否则返回0 int QueueEmpty(LinkQueue Q){if(Q.front==Q.rear)return 1;elsereturn 0;}// 插入元素e为Q的新的队尾元素int EnQueue(LinkQueue *Q,QElemType e){ QueuePtr p=new QNode();//QueuePtr p=(QueuePtr)malloc(sizeof(QNode));if(!p) // 存储分配失败 exit(0);//生成一个以为e为数据域的队列元素p->data=e;p->next=NULL;//将该新队列元素接在队尾的后面(*Q).rear->next=p;(*Q).rear=p;return 1;}// 若队列不空,删除Q的队头元素,用e返回其值,并返回1,否则返回0 int DeQueue(LinkQueue *Q,QElemType *e){QueuePtr p;if((*Q).front==(*Q).rear)return 0;p=(*Q).front->next;//队头元素*e=p->data;(*Q).front->next=p->next;if((*Q).rear==p)(*Q).rear=(*Q).front;delete p;//free(p);return 1;}//销毁队列void DestoryQueue(LinkQueue *Q){while(Q->front){Q->rear = Q->front->next;free(Q->front);Q->front = Q->rear;}}// 从第1个顶点起,按广度优先非递归遍历图G,并对每个顶点调用函数 // Visit一次且仅一次。使用辅助队列Q和访问标志数组visite void BFSTraverse(MulAdjGraph &G,int(*Visit)(const VertexType&)){ int i,u,w;VertexType w1,u1;LinkQueue Q;for(i=0;i<G.ivexNum;++i)G.adjmulist[i].isVisited=false; // 置初值 InitQueue(&Q); // 置空的辅助队列Q for(i=0;i<G.ivexNum;++i)if(!G.adjmulist[i].isVisited) // v尚未访问 {G.adjmulist[i].isVisited=true; // 设置访问标志为1(已访问) Visit(G.adjmulist[i].data);EnQueue(&Q,i); // v入队列 while(!QueueEmpty(Q)) // 队列不空 {DeQueue(&Q,&u); // 队头元素出队并置为u u1=*GetVex(G,u);for(w=FirstAdjVex(G,u1);w>=0;w=NextAdjVex(G,u1,w1)){if(!G.adjmulist[w].isVisited) // w为u的尚未访问的邻接顶点的序号 {G.adjmulist[w].isVisited=true;Visit(G.adjmulist[w].data);EnQueue(&Q,w);}w1=*GetVex(G,w);}}}DestoryQueue(&Q);printf("\n");}// 置边的访问标记为未被访问 void MarkUnvizited(MulAdjGraph &G){int i;ArcType *p;for(i=0;i<G.ivexNum;++i){p=G.adjmulist[i].firstarc;while(p){p->mark=unmarked;if(p->ivex==i)p=p->ilink;elsep=p->jlink;}}}// 输出无向图的邻接多重表Gvoid Display(MulAdjGraph &G){ int i;ArcType *p;MarkUnvizited(G); // 置边的访问标记为未被访问 printf("%d个顶点:\n",G.ivexNum);for(i=0;i<G.ivexNum;++i)printf("%d ",G.adjmulist[i].data);printf("\n%d条边:\n",G.iarcNum);for(i=0;i<G.ivexNum;++i){p=G.adjmulist[i].firstarc;while(p)if(p->ivex==i) // 边的i端与该顶点有关 {if(!p->mark) // 只输出一次 {printf("%d-%d,%8.2f ",G.adjmulist[i].data,G.adjmulist[p->jvex].data,p->weight);p->mark=marked;}p=p->ilink;}else // 边的j端与该顶点有关 {if(!p->mark) // 只输出一次 {printf("%d-%d,%8.2f ",G.adjmulist[p->ivex].data,G.adjmulist[i].data,p->weight);p->mark=marked;}p=p->jlink;}printf("\n");}}int visit(const VertexType &v){printf("%6d",v);return 1;}

test.cpp文件测试代码:

#include "MulAdjGraph.h"
int main(){int k,n;MulAdjGraph g;VertexType v1,v2;CreateGraph(g);Display(g);/***************************** 修改节点 *****************************///printf("修改节点的值,请输入原值 新值: ");scanf("%d %d",&v1,&v2);PutVex(g,v1,v2);Display(g);/**************************** 删除节点 *****************************/printf("删除节点,请输入节点的值: ");scanf("%d",&v1);DeleteVex(g,v1);Display(g);/****************************插入新节点*****************************/printf("插入新节点,请输入节点的值: ");scanf("%d",&v1);InsertVex(g,v1);printf("插入与新节点有关的边,请输入边数: ");scanf("%d",&n);for(k=0;k<n;++k){printf("请输入另一节点的值: ");scanf("%d",&v2);InsertArc(g,v1,v2);}Display(g);/***************************** 搜索 ******************************/printf("深度优先搜索的结果:\n");DFSTraverse(g,visit);printf("广度优先搜索的结果:\n");BFSTraverse(g,visit);DestroyGraph(g);system("pause");return 0; }
测试截图:




1 0
原创粉丝点击