若是您對圖的定義尚不清楚,能夠點此查看關於圖的定義和描述。html
咱們在這裏討論的圖的接口有11個,涉及到8個函數接口和3個宏定義。數據結構
函數接口包括初始化圖、銷燬圖、插入頂點、插入邊、移除頂點、移除邊、取出頂點鄰接表、判斷頂點是否鄰接;宏接口包括返回鄰接表的結構鏈表、返回頂點個數、返回邊個數。函數
void graph_init(Graph *graph, int (*match)(const void *key1,const void *key2), void (*destroy)(void *data));spa
返回值 :無指針
描述:初始化由參數gragh指定的圖。該函數必須在執行其餘與圖相關的操做以前調用。code
match參數指定用來判斷兩個頂點是否匹配的函數,該函數會用在其餘的圖操做中。當key1=key2時,match函數返回1;不然返回0。htm
destroy參數所指定的函數提供一種釋放動態分配數據空間的方法。好比,若是圖包含由malloc動態分配的空間,則destroy應該設置爲free,當銷燬圖時以此來釋放動態分配的內存。對於包含多個動態分配成員的結構化數據,destroy參數應該設置爲一個用戶定義的析構函數,用來對每個動態分配的成員以及結構體自身作資源回收操做。若是圖不包含須要動態釋放空間的數據,destroy參數應該設置爲NULL。blog
複雜度:O(1)接口
void graph_destroy(Graph *graph);內存
返回值:無
描述:銷燬由參數graph所指定的圖。該函數調用後,任何其餘的圖操做都不容許再執行,除非再次初始化。
graph_destroy將圖中全部頂點和邊都移除,若是destroy參數不爲NULL的話,則調用destroy參數所指定的析構函數針對每一個移除的頂點和邊作資源回收操做。
複雜度:O(V+E),這裏V是圖中頂點個數,E是邊的個數。
int graph_ins_vertex(Graph *graph, const void *data);
返回值:若是插入頂點成功則返回0,若是頂點已經存在則返回1,不然返回-1。
描述:將頂點插入由參數graph所指定的圖中。
新頂點包含一個指向data的指針,所以只要頂點還在圖中,data所引用的內存就必須保持有效。由調用者管理data所引用的存儲空間。
複雜度:O(V),這裏V表明圖中的頂點個數。
int graph_ins_edge(Graph *graph, const void *data1, const void *data2);
返回值:若是插入邊成功,返回0;若是邊已經存在,返回1;不然返回-1;
描述:將由data1以及data2所指定的頂點構成的邊插入圖中。data1和data2必須是使用graph_ins_vertex已經插入圖中的頂點。新的邊由data1所指定的頂點的鄰接表中指向data2的指針來表示。所以,只要這條邊還在圖中,data2所引用的內存就必須合法。由調用者負責維護data2所引用的內存空間。
要在無向圖中插入邊(u,v),須要調用該函數兩次:第一次將插入由u到v的邊,第二次插入由v到u的邊。這種類型的表示法對於無向圖來講很廣泛。
複雜度:O(V),這裏V表明圖中的頂點個數。
int graph_rem_vertex(Graph *graph,void **data);
返回值:若是移除頂點成功,返回0,不然返回-1。
描述:從graph指定的圖中移除與data相匹配的頂點。在調用該函數以前,全部與該頂點相關的邊都必須移除。函數返回後,data指向已移除頂點中保存的數據。由調用者負責管理data所引用的存儲空間。
複雜度:O(V+E),這裏V表明圖中頂點個數,而E表明邊的個數。
int graph_rem_edge(Graph *graph, const void *data1, void **data2);
返回值:若是移除成功,返回0;不然返回-1。
描述:從graph指定的圖中移除從data1到data2的邊。函數返回後,data2指向由data1所指定的頂點的鄰接表中保存的數據。由調用者管理data2所引用的存儲空間。
複雜度:O(V),這裏V表明圖中的頂點個數。
int graph_adjlist(const Graph *graph, const void *data, AdjList **adjlist);
返回值:若是取得鄰接表成功,返回0;不然返回-1。
描述:取出graph中由data所指定的頂點的鄰接表。返回的鄰接表以結構體AdjList的形式保存。
該結構體包含與data相匹配的頂點,以及其餘與其鄰接的頂點。函數返回後獲得一個指向鄰接表的指針,所以調用者必須保證不修改該鄰接表中的數據,不然將破壞原始圖中的數據。
複雜度:O(V),這裏V表明圖中的頂點的個數。
int graph_is_adjacent(const Graph *graph, const void *data1, const void *data2);
返回值:若是第二個頂點與第一個頂點鄰接,返回1;不然返回0。
描述:判斷由data2所指定的頂點是否與graph中由data1所指定的頂點鄰接。
複雜度:O(V),這裏V表明圖中的頂點個數。
List graph_adjlists(const Graph *graph);
返回值:由鄰接表結構所組成的鏈表。
描述:這是一個宏,用來返回由參數graph所指定的圖中的鄰接表結構鏈表。
該鏈表中的每個元素都是AdjList結構體。因爲返回的是圖中的鄰接表結構鏈表而不是其拷貝,所以用戶必須保證不對該鏈表作其餘操做。
複雜度:O(1)
int graph_vcount(const Graph *graph);
返回值:圖中頂點的個數。
描述:這是一個宏,用來返回graph所指定的圖中的頂點的個數。
複雜度:O(1)
int graph_ecount(const Graph *graph);
返回值:圖中邊的個數。
描述:這是一個宏,用來計算graph指定的圖中邊的個數。
複雜度:O(1)
咱們經過鄰接錶鏈表結構來表示圖。
鏈表中的每一個結構體都包含兩個成員:一個頂點,以及與該頂點相鄰接的頂點的集合。鏈表中的結點由結構體AdjList表示。
圖的數據結構使用Graph表明。這個結構體包含5個成員:vcount表明圖中的頂點個數;ecount表明圖中邊的個數;match、destroy用來封裝初始化時傳遞給graph_init的函數;adjlists表明鄰接錶鏈表。
咱們爲頂點的顏色屬性定義枚舉類型,這些顏色屬性在圖的操做中很是經常使用。
/*graph.h*/ #ifndef GRAPH_H #define GRAPH_H #include <stdlib.h> #include "list.h" #include "set.h" /*爲鄰接錶鏈表定義成員結構體*/ typedef struct AdjList_ { void *vertex; set adjacent; }AdjList; /*爲圖定義數據結構*/ typedef struct Graph_ { int vcount; int ecount; int (*match)(const void *key1,const void *key2); void (*destroy)(void *data); List adjlist; }Graph; /*使用枚舉類型來定義顏色屬性*/ typedef enum VertexColor_{white,gray,black} VertexColor; /*圖的接口部分*/ void graph_init(Graph *graph, int(*match)(const void *key1,const void *key2),void (*destroy)(void *data)); void graph_destroy(Graph *graph); int graph_ins_vertex(Graph *graph,const void *data); int graph_ins_edge(Graph *graph,const void *data1,const void *data2); int graph_rem_vertex(Graph *graph,void **data); int graph_rem_edge(Graph *graph,void *data1,void **data2); int graph_adjlist(const Graph *graph,const void *data,AdjList **adjlist); int graph_is_adjacent(const Graph *graph,const void *data1,const void *data2); #define graph_adjlists(graph)((graph)->adjlists) #define graph_vcount(graph)((graph)->vcount) #define graph_ecount(graph)((graph)->ecount) #endif // GRAPH_H
/*graph.c*/ #include <stdlib.h> #include <string.h> #include "graph.h" #include "list.h" #include "set.h" /*graph_init 初始化圖*/ void graph_init(Graph *graph,int (*match)(const void *key1,const void key2),void (*destroy)(void *data)) { /*初始化圖結構*/ graph->vcount = 0; graph->ecount = 0; graph->match = match; graph->destroy = destroy; /*初始化鄰接錶鏈表結構*/ list_init(&graph->adjlists,NULL); return; } /*graph_destroy 銷燬圖結構*/ void graph_destroy(Graph *graph) { AdjList *adjlist; /*刪除每個鄰接表結構並將其銷燬*/ while(list_size(&graph->lists)>0) { if(list_rem_next(&graph->adjlists,NULL,(void **)&adjlist)==0) { /*移除頂點集合*/ set_destroy(&adjlist->adjacent); /*釋放頂點成員的內存空間*/ if(graph->destroy != NULL) graph->destroy(adjlist->vertex); /*釋放鄰接表結構*/ free(adjlist); } } /*銷燬鄰接錶鏈表結構*/ list_destroy(&graph->adjlists); /*做爲預防措施清理數據結構*/ memset(graph,0,sizeof(Graph)); return; } /*graph_ins_vertex 將頂點插入圖中*/ /*該函數會將一個AdjList結構體插入鄰接錶鏈表結構中,並將AdjList結構體中的vertex成員指向由調用者傳入的數據*/ int graph_ins_vertex(Graph *graph,const void *data) { ListElmt *element; AdjList *adjlist; int retval; /*首先,要確保頂點不存在於列表中,若是頂點已經存在則返回1*/ for(element=list_head(&graph->adjlists); element != NULL; element=list_next(element)) { if(graph->match(data,((AdjList*)list_data(element))->vertex)) return 1; } /*插入頂點,爲鄰接表分配空間*/ if((adjlist = (AdjList*)malloc(sizeof(AdjList)))==NULL) return -1; /*將adjlist結構中的成員指向傳入的數據data*/ adjlist->vertex=(void*)data; /*初始化一個頂點的鄰接頂點集合*/ set_init(&adjlist->adjacent,graph->match,NULL); /*調用list_ins_next()將結構體插入到鄰接錶鏈表中*/ if((retval = list_ins_next(&graph->adjlists,list_tail(&graph->adjlists),adjlist))!=0) { return retval; } /*調整圖中的頂點統計數據*/ graph->vcount++; return 0; } /*graph_ins_edge 將一條邊插入圖中*/ /*插入從data1到data2的邊,即將data2插入到data1的鄰接表中*/ int graph_ins_edge(Graph *graph,const void *data1,const void *data2) { ListElmt *element; int retval; /*首先,要保證這兩個頂點都存在於圖中*/ for(element = list_head(&graph->adjlists); element != NULL; element = list_next(element)) { if(graph->match(data2,((AdjList *)list_data(element))->vertex)) break; } if(element == NULL) return -1; for(element = list_head(&graph->adjlists); element != NULL; element = list_next(element)) { if(graph->match(data2,((AdjList *)list_data(element))->vertex)) break; } if(element == NULL) return -1; /*將data2的頂點插入到data1的鄰接頂點集合中*/ if((retval = set_insert(&((AdjList *)list_data(element))->adjacent,data2)) !=0 ) { return retval; } /*調整圖中圖的統計數量*/ graph->ecount++; return 0; } /*graph_rem_vertex 將頂點從圖中移除*/ /*該函數將一個AdjList結構體從鄰接表結構鏈表中移除*/ int graph_rem_vertex(Graph *graph,void **data) { ListElmt *element, *temp, *prev; AdjList *adjlist; int found; /*首先確保頂點不存在於任何鄰接表中,但頂點要存在於鄰接表結構鏈表裏,且它的鄰接表爲空*/ prev=NULL; found=0; for(element = list_head(&graph->adjlists); element != NULL; element = list_next(element)) { /*不容許移除鄰接表中的頂點*/ if(set_is_member(&((AdjList *)list_data(element))->adjacent,*data)) return -1; /*找到將要移除的頂點,標記found=1,並使指針temp指向頂點*/ if(graph->match(*data,((AdjList *)list_data(element))->vertex)) { temp=element; found=1; } /*使prev指向目標元素以前的元素*/ if(!found) prev = element; } /*若是未找到要移除的頂點,返回*/ if(!found) return -1; /*若是頂點的鄰接表不爲空,返回*/ if(set_size(&((AdjList *)list_data(temp))->adjacent)>0) return -1; /*知足移除條件,移除頂點*/ if(list_rem_next(&graph->adjlists,prev,(void **)&adjlist) != 0) return -1; /*使*data指向被移除的頂點,釋放鄰接表數據結構的空間*/ *data=adjlist->vertex; free(adjlist); /*調整圖中的頂點統計數*/ graph->vcount--; return 0; } /*graph_rem_edge 將一條邊從圖中移除*/ /*該函數將由data2指定的頂點從data1所指定的頂點的鄰接表中移除*/ int graph_rem_edge(Graph *graph, void *data1, void **data2) { ListElmt *element; /*首先,確保頂點1存在於圖中*/ for(element = list_head(&graph->adjlists); element != NULL; element = list_next(element)) { if(graph->match(data1,((AdjList *)list_data(element))->vertex)) break; } if(element==NULL) return -1; /*經過驗證,調用set_remove()來將data2從data1的鄰接表中移除*/ if(set_remove(&((AdjList *)list_data(element))->adjacent,data2)!=0) return -1; /*最後調整圖中邊的數量*/ graph->ecount--; return 0; } /*graph->adjlist 返回一個AdjList結構體*/ /*返回一個AdjList結構體,其中包含指定頂點的鄰接頂點集合*/ int graph_adjlist(const Graph *graph, const void *data, AdjList **adjlist) { ListElmt *element,*prev; /*找到包含頂點的鏈表元素*/ prev = NULL; for(element = list_head(&graph->adjlists); element != NULL; element = list_next(element)) { if(graph->match(data,((AdjList *)list_data(element))->vertex)) break; prev = element; } if(element == NULL) return -1; /*返回的鄰接表保存到*adjlist*/ *adjlist = list_data(element); return 0; } /*graph_is_adjacent 判斷兩個頂點是否有鄰接關係*/ int graph_is_adjacent(const Graph *graph, const void *data1, const void *data2) { ListElmt *element, *prev; /*首先,在鄰接錶鏈表結構中定位data1所指定的頂點*/ prev=NULL; for(element = list_head(&graph->adjlists); element != NULL; element = list_next(element)) { if(graph->match(data1,((AdjList *)list_data(element))->vertex)) break; prev=element; } if(element==NULL) return 0; /*調用set_is_member來查看data2是否存在於data1的鄰接頂點集合中*/ return set_is_member(&((AdjList *)list_data(element))->adjacent,data2); }