標籤:des style class blog code http
鄰接表:鄰接表是圖的一種鏈式儲存結構。在鄰接表中,對圖中每個頂點建立一個單鏈表,第i個單鏈表中的節點表示依附於頂點vi的邊(對有向圖是以頂點vi為尾的弧)。每個結點有三個域組成,其中鄰接點域指示與頂點vi鄰接的點在途中的位置,鏈域指示下一條邊或者弧的結點;資料域儲存和邊或者弧相關的資訊,如權值等。每個鏈表上附設一個表頭結點。在表頭結點中,除了設定鏈域指向鏈表第一個結點之外,還設定有儲存頂點vi的名。如下所示:
實現:
/**************************************圖的儲存之鄰接表by Rowandjj2014/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,則返回該頂點在圖中位置;否則返回-1bool CreateGraph(ALGraph* G);//採用鄰接表格儲存體結構,構造沒有相關資訊的圖G(用一個函數構造4種圖)void DestroyGraph(ALGraph* G);//銷毀圖Gchar GetVex(ALGraph G,int v);//通過序號v得到頂點名bool PutVex(ALGraph* G,char v,char value);//對v賦新值valueint FirstAdjVex(ALGraph G,char v);//返回頂點v的第一個鄰接頂點的序號int NextAdjVex(ALGraph G,char v,char w);//返回v的(相對於w的)下一個鄰接頂點的序號,若w是v的最後一個鄰接點,則返回-1void 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 20typedef 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<<"請順序輸入每條弧(邊)的權值、弧尾和弧頭:\n"; }else//圖 { cout<<"請順序輸入每條弧(邊)的弧尾和弧頭\n"; } //構造表節點鏈表 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;}
測試:考慮以下有向圖: