邻接表:邻接表是图的一种链式存储结构。在邻接表中,对图中每一个顶点建立一个单链表,第i个单链表中的节点表示依附于顶点vi的边(对有向图是以顶点vi为尾的弧)。每一个结点有三个域组成,当中邻接点域指示与顶点vi邻接的点在途中的位置,链域指示下一条边或者弧的结点;数据域存储和边或者弧相关的信息。如权值等。每一个链表上附设一个表头结点。
在表头结点中。除了设置链域指向链表第一个结点之外,还设置有存储顶点vi的名。例如以下所看到的:
实现:
/************************************** 图的存储之邻接表 by Rowandjj 2014/6/23 **************************************/ #include<iostream> using namespace std; #define MAX_VERTEX_NUM 20//最大顶点数 typedef enum{DG,DN,AG,AN}GraphKind;//有向图、有向网、无向图、无向网 typedef struct _ARCNODE_//表节点(弧) { int adjvex;//邻接点序号 struct _ARCNODE_ *nextarc;//指向下一条弧 int info;//信息(权值) }ArcNode; typedef struct _VNODE_//头结点 { char data;//顶点名 ArcNode *firstarc;//指向第一条弧 }VNode,AdjList[MAX_VERTEX_NUM]; typedef struct _ALGRAPH_//邻接表 { AdjList vertices;//邻接表 int vexnum;//顶点数 int arcnum;//弧数 GraphKind kind;//图的种类 }ALGraph; void (*VisitFunc)(char); //全局函数指针 bool visited[MAX_VERTEX_NUM]; /* 訪问标志数组(全局量) */ void Visit(char p) { cout<<p<<" "; } //-----------------操作------------------------------------- int LocateVex(ALGraph G,char u);//若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 bool CreateGraph(ALGraph* G);//採用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图) void DestroyGraph(ALGraph* G);//销毁图G char GetVex(ALGraph G,int v);//通过序号v得到顶点名 bool PutVex(ALGraph* G,char v,char value);//对v赋新值value int FirstAdjVex(ALGraph G,char v);//返回顶点v的第一个邻接顶点的序号 int NextAdjVex(ALGraph G,char v,char w);//返回v的(相对于w的)下一个邻接顶点的序号,若w是v的最后一个邻接点,则返回-1 void InsertVex(ALGraph* G,char v);//在图G中增添新顶点v(不增添与顶点相关的弧,留待InsertArc()去做) bool DeleteVex(ALGraph* G,char v);//删除G中顶点v及其相关的弧 bool InsertArc(ALGraph* G,char v,char w);//在G中增添弧<v,w>,若G是无向的,则还增添对称弧<w,v> bool DeleteArc(ALGraph* G,char v,char w);//在G中删除弧<v,w>,若G是无向的,则还删除对称弧<w,v> void DFSTravel(ALGraph* G,void (*Visit)(char));//深度优先 void DFS(ALGraph G,int v); void BFSTravel(ALGraph G,void (*Visit)(char));//广度优先 void Display(ALGraph G);//打印图 //----------------辅助队列------------------------------------------ #define MAX_QUEUE_SIZE 20 typedef struct _QUEUENODE_ { int data; struct _QUEUENODE_ *next; }QueueNode; typedef struct _QUEUE_ { QueueNode *pHead; QueueNode *pTail; int size; }Queue; bool InitQueue(Queue *Q); bool DestroyQueue(Queue *Q); bool DeQueue(Queue *Q,int* e); bool EnQueue(Queue *Q, int e); bool QueueEmpty(Queue Q); //------------------------------------------------------------------ bool InitQueue(Queue *Q) { Q->pHead = Q->pTail = (QueueNode *)malloc(sizeof(QueueNode)); if(!Q->pHead) { return false; } Q->pHead->next = NULL; Q->size = 0; return true; } bool EnQueue(Queue *Q, int e) { QueueNode *node = (QueueNode*)malloc(sizeof(QueueNode)); node->data = e; node->next = NULL; Q->pTail->next = node; Q->pTail = node; Q->size++; return true; } bool DeQueue(Queue *Q,int* e) { QueueNode *node = Q->pHead->next; if(node) { *e = node->data; Q->pHead->next = node->next; if(Q->pTail == node) { Q->pTail = Q->pHead; } free(node); Q->size--; } return true; } bool QueueEmpty(Queue Q) { return Q.size == 0; } bool DestroyQueue(Queue *Q) { QueueNode *pTemp = Q->pHead->next; while(pTemp != NULL) { Q->pHead->next = pTemp->next; free(pTemp); pTemp = Q->pHead->next; } free(Q->pHead); Q->size = 0; return true; } //------------------------------------------------------------------ int LocateVex(ALGraph G,char u) { int i; for(i = 0; i < G.vexnum; i++) { if(u == G.vertices[i].data) { return i; } } return -1; } bool CreateGraph(ALGraph* G) { int i,j,k; int w;//权值 char va,vb;//弧尾、弧头 ArcNode *p;//弧 cout<<"请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): "; scanf("%d",&(*G).kind); cout<<"请输入图的顶点数,边数: "; cin>>G->vexnum; cin>>G->arcnum; cout<<"请输入顶点值:"<<endl; //构造顶点 for(i = 0; i < G->vexnum; i++) { cin>>G->vertices[i].data; G->vertices[i].firstarc = NULL; } if(G->kind == 1 || G->kind == 3)//网 { cout<<"请顺序输入每条弧(边)的权值、弧尾和弧头: "; }else//图 { cout<<"请顺序输入每条弧(边)的弧尾和弧头 "; } //构造表节点链表 for(k = 0; k < G->arcnum; k++) { if(G->kind == 1 || G->kind == 3)//网 { cin>>w; cin>>va; cin>>vb; }else//图 { cin>>va; cin>>vb; } //定位弧尾弧头的位置 i = LocateVex(*G,va); j = LocateVex(*G,vb); p = (ArcNode *)malloc(sizeof(ArcNode)); p->adjvex = j; if(G->kind == 1 || G->kind == 3)//网 { p->info = w;//权值 }else { p->info = NULL; } //插入表 p->nextarc = G->vertices[i].firstarc;//插在表头 G->vertices[i].firstarc = p; //假设是无向图或者无向网。还须要添加对称结点 if(G->kind == 2 || G->kind == 3) { p = (ArcNode *)malloc(sizeof(ArcNode)); p->adjvex = i; if(G->kind == 3)//若是无向网。还须要权值 { p->info = w; }else { p->info = NULL; } //插入表 p->nextarc = G->vertices[j].firstarc; G->vertices[j].firstarc = p; } } return true; } void Display(ALGraph G) { ArcNode *p; int i; switch(G.kind) { case DG: cout<<"有向图"; break; case AG: cout<<"无向图"; break; case DN: cout<<"有向网"; break; case AN: cout<<"无向网"; break; default: break; } cout<<endl; cout<<"顶点:"<<endl; for(i = 0; i < G.vexnum; i++) { cout<<G.vertices[i].data<<" "; } cout<<endl; //边 cout<<"边:"<<endl; for(i = 0; i < G.vexnum; i++) { p = G.vertices[i].firstarc; while(p) { if(G.kind == 0 || G.kind == 1)//有向 { cout<<G.vertices[i].data<<" "<<G.vertices[p->adjvex].data; if(G.kind == 1)//有向网 { cout<<" "<<p->info; } }else//无向 { if(i < p->adjvex)//不反复打印 { cout<<G.vertices[i].data<<" "<<G.vertices[p->adjvex].data; if(G.kind == 3)//无向网 { cout<<" "<<p->info; } } } cout<<endl; p = p->nextarc; } } } void DestroyGraph(ALGraph* G) { ArcNode *p,*q; int i; for(i = 0; i < G->vexnum; i++) { p = G->vertices[i].firstarc; while(p) { q = p->nextarc; free(p); p = q; } } G->arcnum = 0; G->vexnum = 0; } char GetVex(ALGraph G,int v) { if(v>=G.vexnum || v<0) { exit(0); } return G.vertices[v].data; } bool PutVex(ALGraph* G,char v,char value) { int i = LocateVex(*G,v); if(i == -1) { return false; } G->vertices[i].data = value; return true; } int FirstAdjVex(ALGraph G,char v) { int i = LocateVex(G,v); if(i < 0) { return -1; } ArcNode *arcNode = G.vertices[i].firstarc; if(arcNode == NULL) { return -1; } return arcNode->adjvex; } int NextAdjVex(ALGraph G,char v,char w) { int i,j; i = LocateVex(G,v); j = LocateVex(G,w); ArcNode *p = G.vertices[i].firstarc; while(p && p->adjvex != j) { p = p->nextarc; } if(!p || !p->nextarc)//没找到w或w是最后一个邻接点 { return -1; } else { return p->nextarc->adjvex; } } void InsertVex(ALGraph* G,char v) { G->vertices[G->vexnum].data = v; G->vertices[G->vexnum].firstarc = NULL; G->vexnum++; } bool DeleteVex(ALGraph* G,char v) { int i,j; ArcNode *p,*q; //1.删除邻接表中顶点为v的那一行全部数据,更改弧总数,顶点总数 i = LocateVex(*G,v); if(i < 0 || i >= G->vexnum)//不合法的位置 { return false; } p = G->vertices[i].firstarc; while(p)//依次删除弧 { q = p->nextarc; free(p); p = q; G->arcnum--; } G->vexnum--; //2.更改顶点v之后的顶点在数组中的位置(前移一位) for(j = i; j < G->vexnum; j++) { G->vertices[j] = G->vertices[j+1]; } //3.遍历剩下的邻接表,找到包括顶点v的弧或者边。删除之。另外须要注意,对遍历的每一个弧/边,视情况更新序号 for(j = 0; j < G->vexnum; j++) { p = G->vertices[j].firstarc;//p指向遍历的顶点的第一条弧或者边 while(p) { if(p->adjvex == i)//假设找到指向已删除顶点的弧或者边 { if(p == G->vertices[j].firstarc)//假设待删除的结点是第一个结点 { G->vertices[j].firstarc = p->nextarc; free(p); p = G->vertices[j].firstarc; if(G->kind <= 1)//假设是有向的,则还需更改弧数 { G->arcnum--; } }else//不是第一个结点 { q->nextarc = p->nextarc; free(p); p = q->nextarc; if(G->kind <= 1)//假设是有向的,则还需更改弧数 { G->arcnum--; } } }else//假设当前弧并非要找的弧,那么继续向后遍历 { if(p->adjvex > i)//(非常关键)更新序号 { p->adjvex--; } q = p; p = p->nextarc;//指向下一条弧 } } } return true; } bool InsertArc(ALGraph* G,char v,char w) { int i,j,weight; ArcNode *arcNode; //1.得到v、w的在邻接表中的序号 i = LocateVex(*G,v); j = LocateVex(*G,w); if(i<0 || j<0) { return false; } G->arcnum++; if(G->kind == 1 || G->kind == 3) { cout<<"输入权值:"; cin>>weight;//输入权值 } //2.生成一个弧结点,插入到顶点v的第一个邻接点的位置(假设是网的话,须要用户输入权值) arcNode = (ArcNode*)malloc(sizeof(ArcNode)); arcNode->adjvex = j; if(G->kind == 1 || G->kind == 3) { arcNode->info = weight; } else { arcNode->info = NULL; } arcNode->nextarc = G->vertices[i].firstarc; G->vertices[i].firstarc = arcNode; //3.假设是无向的,那么还需生成对称节点,并插到合适位置 if(G->kind >= 2) { arcNode = (ArcNode *)malloc(sizeof(ArcNode)); arcNode->adjvex = i; if(G->kind == 3)//无向网 { arcNode->info = weight; } else { arcNode->info = NULL; } arcNode->nextarc = G->vertices[j].firstarc; G->vertices[j].firstarc = arcNode; } return true; } bool DeleteArc(ALGraph* G,char v,char w) { int i,j; ArcNode *p,*q; //1.得到v、w的在邻接表中的序号 i = LocateVex(*G,v); j = LocateVex(*G,w); if(i < 0 || j < 0) { return false; } //2.删除v-w p = G->vertices[i].firstarc; while(p && p->adjvex!=j) { q = p; p = p->nextarc; } if(p && p->adjvex==j)//找到弧<v-w> { if(p == G->vertices[i].firstarc)//p指的是第一条弧 { G->vertices[i].firstarc = p->nextarc; } else { q->nextarc = p->nextarc; } free(p); G->arcnum--; } //3.若是无向,则还删除w-v if(G->kind >= 2) { p = G->vertices[j].firstarc; while(p && p->adjvex!=i) { q = p; p = p->nextarc; } if(p && p->adjvex==i)//找到弧<w-v> { if(p == G->vertices[j].firstarc)//p指的是第一条弧 { G->vertices[j].firstarc = p->nextarc; } else { q->nextarc = p->nextarc; } free(p); } } return true; } void DFSTravel(ALGraph* G,void (*Visit)(char)) { int i; VisitFunc = Visit; for(i = 0; i < G->vexnum; i++) { visited[i] = false; } for(i = 0; i < G->vexnum; i++) { if(!visited[i]) { DFS(*G,i); } } cout<<endl; } void DFS(ALGraph G,int v) { int i; char v1,w1; v1 = GetVex(G,v); visited[v] = true; VisitFunc(G.vertices[v].data); for(i = FirstAdjVex(G,v1);i>=0; i = NextAdjVex(G,v1,w1 = GetVex(G,i))) { if(!visited[i]) { DFS(G,i); } } } void BFSTravel(ALGraph G,void (*Visit)(char)) { Queue q; InitQueue(&q); char w1,u1; int i,u,w; for(i = 0; i < G.vexnum; i++) { visited[i] = false; } for(i = 0; i < G.vexnum; i++) { if(!visited[i]) { visited[i] = true; Visit(G.vertices[i].data); EnQueue(&q,i); while(!QueueEmpty(q)) { DeQueue(&q,&u); u1 = GetVex(G,u); for(w = FirstAdjVex(G,u1);w>=0;w = NextAdjVex(G,u1,w1=GetVex(G,w))) { if(!visited[w]) { visited[w] = true; Visit(G.vertices[w].data); EnQueue(&q,w); } } } } } DestroyQueue(&q); cout<<endl; } int main() { ALGraph graph; CreateGraph(&graph); Display(graph); cout<<"深度优先:"<<endl; DFSTravel(&graph,Visit); cout<<"广度优先:"<<endl; BFSTravel(graph,Visit); DestroyGraph(&graph); return 0; }
測试:
考虑下面有向图:
版权声明:本文博客原创文章,博客,未经同意,不得转载。