// 圖的數組(鄰接矩陣)存儲表示 #include#include #include #define MAX_NAME 3 // 頂點字符串的最大長度+1 #define MAX_VERTEX_NUM 20 typedef int InfoType; // 存放網的權值 typedef char VertexType[MAX_NAME]; // 字符串類型 typedef enum{DG, DN, AG, AN}GraphKind; // {有向圖,有向網,無向圖,無向網} int visited[MAX_VERTEX_NUM]; // 訪問標志數組 void(*VisitFunc)(char* v); // 函數變量 typedef struct ArcNode { int adjvex; // 該弧所指向的頂點的位置 struct ArcNode *nextarc; // 指向下一條弧的指針 InfoType *info; // 網的權值指針 }ArcNode; // 表結點 typedef struct VNode { VertexType data; // 頂點信息 ArcNode *firstarc; // 第一個表結點的地址,指向第一條依附該頂點的弧的指針 }VNode, AdjList[MAX_VERTEX_NUM];// 頭結點 typedef struct { AdjList vertices; int vexnum, arcnum; // 圖的當前頂點數和弧數 int kind; // 圖的種類標志 }ALGraph; typedef int QElemType; // 隊列類型 //單鏈隊列--隊列的鏈式存儲結構 typedef struct QNode { QElemType data; //數據域 struct QNode *next; //指針域 }QNode, *QueuePtr; typedef struct { QueuePtr front, rear; //隊頭指針,指針域指向隊頭元素 隊尾指針,指向隊尾元素 }LinkQueue; //定位功能 // 若G中存在頂點u,則返回該頂點在圖中位置;否則返回-1。 int LocateVex(ALGraph G,VertexType u) { int i; for(i=0;i adjvex = j; if((*G).kind == 1 || (*G).kind == 3) // 網 { p->info = (int *)malloc(sizeof(int)); *(p->info) = w; } else p->info = NULL; // 圖 p->nextarc = (*G).vertices[i].firstarc; // 插在表頭 (*G).vertices[i].firstarc = p; if((*G).kind >= 2) // 無向圖或網,產生第二個表結點 { p = (ArcNode*)malloc(sizeof(ArcNode)); p->adjvex = i; if((*G).kind == 3) // 無向網 { p->info = (int*)malloc(sizeof(int)); *(p->info) = w; } else p->info = NULL; // 無向圖 p->nextarc = (*G).vertices[j].firstarc; // 插在表頭 (*G).vertices[j].firstarc = p; } } return 1; } // 銷毀圖G void DestroyGraph(ALGraph *G) { int i; ArcNode *p,*q; for(i = 0;i < (*G).vexnum; ++i) { p = (*G).vertices[i].firstarc; while(p) { q = p->nextarc; if((*G).kind%2) // 網 free(p->info); free(p); p=q; } } (*G).vexnum=0; (*G).arcnum=0; } // 返回v的值。 VertexType* GetVex(ALGraph G, int v) { if(v>=G.vexnum||v<0) exit(0); return &G.vertices[v].data; } // 對v賦新值value。 int PutVex(ALGraph *G,VertexType v,VertexType value) { int i; i=LocateVex(*G,v); if(i > -1) // v是G的頂點 { strcpy((*G).vertices[i].data,value); return 1; } return 0; } // 返回v的第一個鄰接頂點的序號。若頂點在G中沒有鄰接頂點,則返回-1。 int FirstAdjVex(ALGraph G,VertexType v) { ArcNode *p; int v1; v1 = LocateVex(G,v); // v1為頂點v在圖G中的序號 p = G.vertices[v1].firstarc; if(p) return p->adjvex; else return -1; } // 返回v的(相對於w的)下一個鄰接頂點的序號。若w是v的最後一個 // 鄰接點, 則返回-1。 int NextAdjVex(ALGraph G,VertexType v,VertexType w) { ArcNode *p; int v1,w1; v1 = LocateVex(G,v); // v1為頂點v在圖G中的序號 w1 = LocateVex(G,w); // w1為頂點w在圖G中的序號 p = G.vertices[v1].firstarc; while(p&&p->adjvex != w1) // 指針p不空且所指表結點不是w p = p->nextarc; if(!p||!p->nextarc) // 沒找到w或w是最後一個鄰接點 return -1; else // p->adjvex==w // 返回v的(相對於w的)下一個鄰接頂點的序號 return p->nextarc->adjvex; } // 在圖G中增添新頂點v(不增添與頂點相關的弧,留待InsertArc()去做)。 void InsertVex(ALGraph *G,VertexType v) { strcpy((*G).vertices[(*G).vexnum].data,v); // 構造新頂點向量 (*G).vertices[(*G).vexnum].firstarc=NULL; (*G).vexnum++; // 圖G的頂點數加1 } // 刪除G中頂點v及其相關的弧。 int DeleteVex(ALGraph *G,VertexType v) { int i,j; ArcNode *p,*q; j=LocateVex(*G,v); // j是頂點v的序號 if(j < 0 ) // v不是圖G的頂點 return 0; p = (*G).vertices[j].firstarc; // 刪除以v為出度的弧或邊 while( p ) { q = p; p = p->nextarc; if((*G).kind % 2) // 網 free(q->info); free(q); (*G).arcnum--; // 弧或邊數減1 } (*G).vexnum--; // 頂點數減1 for(i = j; i < (*G).vexnum; i++) // 頂點v後面的頂點前移 (*G).vertices[i] = (*G).vertices[i+1]; // 刪除以v為入度的弧或邊且必要時修改表結點的頂點位置值 for(i = 0; i < (*G).vexnum; i++) { p = (*G).vertices[i].firstarc; // 指向第1條弧或邊 while(p) // 有弧 { if(p->adjvex == j) // 是以v為入度的邊。 { if(p == (*G).vertices[i].firstarc) // 待刪結點是第1個結點 { (*G).vertices[i].firstarc = p->nextarc; if((*G).kind % 2) // 網 free(p->info); free(p); p = (*G).vertices[i].firstarc; if((*G).kind < 2) // 有向 (*G).arcnum--; // 弧或邊數減1 } else { q->nextarc = p->nextarc; if((*G).kind%2) // 網 free(p->info); free(p); p = q->nextarc; if((*G).kind < 2) // 有向 (*G).arcnum--; // 弧或邊數減1 } } else { if(p->adjvex > j) p->adjvex--; // 修改表結點的頂點位置值(序號) q = p; p = p->nextarc; } } } return 1; } // 在G中增添弧 ,若G是無向的,則還增添對稱弧 。 int InsertArc(ALGraph *G,VertexType v, VertexType w) { ArcNode *p; int w1,i,j; i=LocateVex(*G,v); // 弧尾或邊的序號 j=LocateVex(*G,w); // 弧頭或邊的序號 if(i < 0 || j < 0) return 0; (*G).arcnum++; // 圖G的弧或邊的數目加1 if((*G).kind%2) // 網 { printf(請輸入弧(邊)%s→%s的權值: ,v,w); scanf(%d,&w1); } p=(ArcNode*)malloc(sizeof(ArcNode)); p->adjvex=j; if((*G).kind%2) // 網 { p->info=(int*)malloc(sizeof(int)); *(p->info)=w1; } else p->info = NULL; p->nextarc = (*G).vertices[i].firstarc; // 插在表頭 (*G).vertices[i].firstarc = p; if((*G).kind >= 2) // 無向,生成另一個表結點 { p = (ArcNode*)malloc(sizeof(ArcNode)); p->adjvex = i; if((*G).kind == 3) // 無向網 { p->info = (int*)malloc(sizeof(int)); *(p->info) = w1; } else p->info=NULL; p->nextarc=(*G).vertices[j].firstarc; // 插在表頭 (*G).vertices[j].firstarc=p; } return 1; } // 在G中刪除弧 ,若G是無向的,則還刪除對稱弧 。 int DeleteArc(ALGraph *G,VertexType v,VertexType w) { ArcNode *p,*q; int i,j; i = LocateVex(*G,v); // i是頂點v(弧尾)的序號 j = LocateVex(*G,w); // j是頂點w(弧頭)的序號 if(i < 0 || j < 0 || i == j) return 0; p=(*G).vertices[i].firstarc; // p指向頂點v的第一條出弧 while(p&&p->adjvex!=j) // p不空且所指之弧不是待刪除弧 { // p指向下一條弧 q=p; p=p->nextarc; } if(p&&p->adjvex==j) // 找到弧 { if(p==(*G).vertices[i].firstarc) // p所指是第1條弧 (*G).vertices[i].firstarc=p->nextarc; // 指向下一條弧 else q->nextarc=p->nextarc; // 指向下一條弧 if((*G).kind%2) // 網 free(p->info); free(p); // 釋放此結點 (*G).arcnum--; // 弧或邊數減1 } if((*G).kind>=2) // 無向,刪除對稱弧 { p=(*G).vertices[j].firstarc; // p指隙サ鉾的第一條出弧 while(p&&p->adjvex!=i) // p不空且所指之弧不是待刪除弧 { // p指向下一條弧 q=p; p=p->nextarc; } if(p&&p->adjvex==i) // 找到弧 { if(p==(*G).vertices[j].firstarc) // p所指是第1條弧 (*G).vertices[j].firstarc=p->nextarc; // 指向下一條弧 else q->nextarc=p->nextarc; // 指向下一條弧 if((*G).kind==3) // 無向網 free(p->info); free(p); // 釋放此結點 } } return 1; } // 從第v個頂點出發遞歸地深度優先遍歷圖G。 void DFS(ALGraph G,int v) { int w; VertexType v1,w1; strcpy(v1,*GetVex(G,v)); visited[v] = 1; // 設置訪問標志為1(已訪問) VisitFunc(G.vertices[v].data); // 訪問第v個頂點 for(w = FirstAdjVex(G,v1); w >= 0; w = NextAdjVex(G,v1,strcpy(w1,*GetVex(G,w)))) if(!visited[w]) DFS(G,w); // 對v的尚未訪問的鄰接點w遞歸調用DFS } // 對圖G作深度優先遍歷。 void DFSTraverse(ALGraph G,void(*Visit)(char*)) { int v; // 使用全局變量VisitFunc,使DFS不必設函數指針參數 VisitFunc = Visit; for(v = 0; v < G.vexnum; v++) visited[v] = 0; // 訪問標志數組初始化 for(v = 0; v < G.vexnum; v++) if(!visited[v]) DFS(G,v); // 對尚未訪問的頂點調用DFS printf( ); } // 構造一個空隊列Q。 int InitQueue(LinkQueue *Q) { (*Q).front = (*Q).rear = (QueuePtr)malloc(sizeof(QNode)); //動態分配一個空間 if(!(*Q).front) exit(0); (*Q).front->next = NULL; //隊頭指針指向空,無數據域,這樣構成了一個空隊列 return 1; } // 插入元素e為Q的新的隊尾元素。 int EnQueue(LinkQueue *Q, QElemType e) { 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; free(p); return 1; } // 若Q為空隊列,則返回1,否則返回0。 int QueueEmpty(LinkQueue Q) { if(Q.front == Q.rear) return 1; else return 0; } //按廣度優先遍歷圖G。使用輔助隊列Q和訪問標志數組visited。 void BFSTraverse(ALGraph G,void(*Visit)(char*)) { int v,u,w; VertexType u1,w1; LinkQueue Q; for(v = 0; v < G.vexnum; ++v) visited[v]=0; // 置初值 InitQueue(&Q); // 置空的輔助隊列Q for(v = 0; v < G.vexnum; v++) // 如果是連通圖,只v=0就遍歷全圖 if(!visited[v]) // v尚未訪問 { visited[v]=1; Visit(G.vertices[v].data); EnQueue(&Q,v); // v入隊列 while(!QueueEmpty(Q)) // 隊列不空 { DeQueue(&Q,&u); // 隊頭元素出隊並置為u strcpy(u1,*GetVex(G,u)); for(w = FirstAdjVex(G,u1); w >= 0; w = NextAdjVex(G, u1, strcpy(w1, *GetVex(G,w)))) if(!visited[w]) // w為u的尚未訪問的鄰接頂點 { visited[w] = 1; Visit(G.vertices[w].data); EnQueue(&Q,w); // w入隊 } } } printf( ); } // 輸出圖的鄰接表G。 void Display(ALGraph G) { int i; ArcNode *p; switch(G.kind) { case DG: printf(有向圖 ); break; case DN: printf(有向網 ); break; case AG: printf(無向圖 ); break; case AN: printf(無向網 ); } printf(%d個頂點: ,G.vexnum); for(i = 0; i < G.vexnum; ++i) printf(%s ,G.vertices[i].data); printf( %d條弧(邊): , G.arcnum); for(i = 0; i < G.vexnum; i++) { p = G.vertices[i].firstarc; while(p) { if(G.kind <= 1) // 有向 { printf(%s→%s ,G.vertices[i].data, G.vertices[p->adjvex].data); if(G.kind == DN) // 網 printf(:%d , *(p->info)); } else // 無向(避免輸出兩次) { if(i < p->adjvex) { printf(%s-%s ,G.vertices[i].data, G.vertices[p->adjvex].data); if(G.kind == AN) // 網 printf(:%d ,*(p->info)); } } p=p->nextarc; } printf( ); } } void print(char *i) { printf(%s ,i); } int main() { int i,j,k,n; ALGraph g; VertexType v1,v2; printf(請選擇有向圖 ); CreateGraph(&g); Display(g); printf(刪除一條邊或弧,請輸入待刪除邊或弧的弧尾 弧頭: ); scanf(%s%s,v1,v2); DeleteArc(&g,v1,v2); Display(g); printf(修改頂點的值,請輸入原值 新值: ); scanf(%s%s,v1,v2); PutVex(&g,v1,v2); Display(g); printf(插入新頂點,請輸入頂點的值: ); scanf(%s,v1); InsertVex(&g,v1); Display(g); printf(插入與新頂點有關的弧或邊,請輸入弧或邊數目: ); scanf(%d,&n); for(k=0;k
效果: